我正在尝试测试具有这种结构的具体对象。
class Database {
public:
Database(Server server) : server_(server) {}
int Query(const char* expression) {
server_.Connect();
return server_.ExecuteQuery();
}
private:
Server server_;
};
即它没有虚函数,更不用说定义明确的接口(interface)了。
我想要一个调用模拟服务进行测试的假数据库。更糟糕的是,我希望针对真实版本或伪造版本构建相同的代码,以便相同的测试代码可以:
- 测试真实的数据库实现 - 用于集成测试
- 测试调用模拟服务的假实现
为了解决这个问题,我使用了一个模板化的假对象,就像这样:
#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_;
};
编辑:注意假数据库必须调用真实数据库(但使用模拟服务器)。
现在为了在它们之间切换,我正在计划以下测试框架:
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;
}
};
我不太喜欢编译时真假之间的切换。
所以,我的问题是:
- 是否有更好的方法在 Database 和 FakeDatabase 之间切换?例如,是否有可能在运行时以干净的方式完成它?我喜欢避免#ifdefs。
- 此外,如果有人有更好的方法来制作模仿具体类的假类,我将不胜感激。
我不想在整个实际测试代码(QueryTestCase 类)中使用模板化代码。
您也可以随意批评代码风格本身。你可以看到一个 compiled version of this code on codepad .
最佳答案
免责声明我在 Typemock 工作。
在下面的示例中,代码是针对真实版本构建的,生产代码没有任何变化。
正在测试中:
class Server
{
public:
void Connect(){/* real implementation*/}
int ExecuteQuery() { return 0; // real implementation }
};
class Database{
public:
Database(Server server) : server_(server) {}
int Query(const char* expression) {
server_.Connect();
return server_.ExecuteQuery();
}
private:
Server server_;
};
测试类:
TEST_CLASS(GlobalCMethods)
{
public:
测试真实的数据库实现 - 用于集成测试: 测试真实的数据库实现意味着像在真实代码中一样使用它。下面的例子就是这样做的。但是,您可以在不更改数据库逻辑本身的情况下使用模拟来更改某些逻辑。 例如,如果查询执行是有条件的,您可以模拟该条件(不是在本例中)并仍然执行真正的查询。
TEST_METHOD(IntegrationTest)
{
Server server;
Database db(server);
int QueryResult = db.Query("dummy expression");
Assert::AreEqual(0, QueryResult);
}
测试调用模拟服务的假实现: 当您的数据库使用 Server 类进行查询时,模拟 Server 基本上会将真正的连接从游戏中移除。 我使用 Typemock Isolator++ 进行模拟。我用了FAKE_ALL模拟 Server 的实例化,因为在您的示例中 Server 是按值传递的(不是一个好主意)并且它在传递时被创建和复制。 使用 FAKE_ALL 时,您会获得一个代理 (fakeServer) 到替换代码中服务器的对象。在代理上设置的所有行为都适用于您代码中的对象 (server_)。
TEST_METHOD(UnitTest)
{
Server* fakeServer = FAKE_ALL<Server>();
WHEN_CALLED(fakeServer->ExecuteQuery()).Return(3);
Database db(*fakeServer);
int QueryResult = db.Query("dummy expression");
Assert::AreEqual(3, QueryResult);
}
};
关于c++ - 模拟具体类 : templates and avoiding conditional compilation,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2654718/