Mocking a concrete class : templates and avoiding conditional compilation
- by AshirusNW
I'm trying to testing a concrete object with this sort of structure.
class Database {
public:
Database(Server server) : server_(server) {}
int Query(const char* expression) {
server_.Connect();
return server_.ExecuteQuery();
}
private:
Server server_;
};
i.e. it has no virtual functions, let alone a well-defined interface.
I want to a fake database which calls mock services for testing. Even worse, I want the same code to be either built against the real version or the fake so that the same testing code can both:
Test the real Database implementation - for integration tests
Test the fake implementation, which calls mock services
To solve this, I'm using a templated fake, like this:
#ifndef INTEGRATION_TESTS
class FakeDatabase {
public:
FakeDatabase() : realDb_(mockServer_) {}
int Query(const char* expression) {
MOCK_EXPECT_CALL(mockServer_, Query, 3);
return realDb_.Query();
}
private:
// in non-INTEGRATION_TESTS builds, Server is a mock Server with
// extra testing methods that allows mocking
Server mockServer_;
Database realDb_;
};
#endif
template <class T>
class TestDatabaseContainer {
public:
int Query(const char* expression) {
int result = database_.Query(expression);
std::cout << "LOG: " << result << endl;
return result;
}
private:
T database_;
};
Edit: Note the fake Database must call the real Database (but with a mock Server).
Now to switch between them I'm planning the following test framework:
class DatabaseTests {
public:
#ifdef INTEGRATION_TESTS
typedef TestDatabaseContainer<Database> TestDatabase ;
#else
typedef TestDatabaseContainer<FakeDatabase> TestDatabase ;
#endif
TestDatabase& GetDb() { return _testDatabase; }
private:
TestDatabase _testDatabase;
};
class QueryTestCase : public DatabaseTests {
public:
void TestStep1() {
ASSERT(GetDb().Query(static_cast<const char *>("")) == 3);
return;
}
};
I'm not a big fan of that compile-time switching between the real and the fake.
So, my question is:
Whether there's a better way of switching between Database and FakeDatabase? For instance, is it possible to do it at runtime in a clean fashion? I like to avoid #ifdefs.
Also, if anyone has a better way of making a fake class that mimics a concrete class, I'd appreciate it.
I don't want to have templated code all over the actual test code (QueryTestCase class).
Feel free to critique the code style itself, too. You can see a compiled version of this code on codepad.