Skip to content

Instantly share code, notes, and snippets.

@alamaison
Created February 2, 2011 13:24
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save alamaison/807670 to your computer and use it in GitHub Desktop.
Save alamaison/807670 to your computer and use it in GitHub Desktop.
Preferred C++ version using templates
#include <algorithm>
#include <iostream>
#include <set>
#include <string>
#include <vector>
using std::cerr;
using std::cout;
using std::set;
using std::string;
using std::vector;
class Data {};
class User
{
public:
User(const string& /*name*/, int /*shoe_size*/) {}
void setGravatar(const string& /*grav*/) {}
void setProfilePage(const string& /*profile*/) {}
Data locallyCachedNewData() { return Data(); }
};
/**
* Our database knows all the things we need, but makes costly calls to find it
*/
class Database
{
public:
set<string> getAllUserNames()
{
cout << "doSomeLengthyDatabaseLookupProcedureThatMakesThisCallTakeMore"
"ThanThreeAndAHalfMillseconds()" << std::endl;
set<string> names;
names.insert("Harry");
names.insert("Sally");
return names;
}
User getUser(string /*username*/)
{
cout << "lookupAllTheUsersDataInTheLocalDatabase()" << std::endl;
return User("Harry", 12);
}
};
/**
* Our adapter talks to some website. It's an expensive set of ops to run.
*/
class ApiAdapter
{
public:
string getGravatar(const string& /*name*/)
{
cout << "communicateToNetwork()" << std::endl;
return "http://gravatar.example.com";
}
string getProfilePage(const string& /*name*/)
{
cout << "communicateToNetwork()" << std::endl;
return "http://profile.example.com";
}
void updateWithLocallyCachedNewData(string /*name*/, Data /*newData*/)
{
cout << "communicateToNetwork()" << std::endl;
}
};
template<typename A, typename D>
class MyModelClass
{
private:
A mAdapter;
D mDatabase;
public:
MyModelClass(A adapter, D someDatabaseINeedToAccess) :
mAdapter(adapter), mDatabase(someDatabaseINeedToAccess) {}
/*
* This is the method we want to test
*/
void populateTheDatabaseWithSomeInfo()
{
set<string> names = mDatabase.getAllUserNames();
set<string>::iterator name;
for (name = names.begin(); name != names.end(); ++name)
{
User localUserData = mDatabase.getUser(*name);
localUserData.setGravatar(mAdapter.getGravatar(*name));
localUserData.setProfilePage(mAdapter.getProfilePage(*name));
mAdapter.updateWithLocallyCachedNewData(
*name, localUserData.locallyCachedNewData());
}
}
};
class ActionRecorder
{
vector<string> m_actions;
public:
void recordAction(const string& action)
{
m_actions.push_back(action);
}
bool contains(const string& action)
{
return std::find(
m_actions.begin(), m_actions.end(), action) != m_actions.end();
}
};
class MockDatabase
{
ActionRecorder& mActionRecorder;
public:
MockDatabase(ActionRecorder& recorder) : mActionRecorder(recorder) {}
set<string> getAllUserNames()
{
mActionRecorder.recordAction("get names");
set<string> userNameSet;
userNameSet.insert("James");
return userNameSet;
}
User getUser(const string& name)
{
mActionRecorder.recordAction("checked for user " + name);
// Our test only has James
return User("James", 5);
}
};
class MockApiAdapter
{
ActionRecorder& mActionRecorder;
public:
MockApiAdapter(ActionRecorder& recorder) : mActionRecorder(recorder) {}
string getGravatar(string name)
{
mActionRecorder.recordAction("lookup gravatar " + name);
return "http://gravatar.example.com";
}
string getProfilePage(string name)
{
mActionRecorder.recordAction("lookup profile page " + name);
return "http://profile.example.com";
}
void updateWithLocallyCachedNewData(string /*name*/, Data /*newData*/) {}
};
void my_assert(bool condition, const string& description)
{
if (!condition)
cerr << "ASSERT FAILED: " << description << std::endl;
}
#define MY_ASSERT(x) my_assert((x), #x);
int main()
{
ActionRecorder ar; // We'll write to this
// Instantiate our testing object using our mocks
MockApiAdapter adapter(ar);
MockDatabase db(ar);
MyModelClass<MockApiAdapter, MockDatabase> objForQuickTesting(adapter, db);
objForQuickTesting.populateTheDatabaseWithSomeInfo();
// It really does pull the names from the database
MY_ASSERT(ar.contains("get names"));
// It really does actually check the names we supplied it from the DB
MY_ASSERT(ar.contains("checked for user James"));
MY_ASSERT(ar.contains("lookup gravatar James"));
MY_ASSERT(ar.contains("lookup profile page James"));
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment