c++ - 模拟具体类 : templates and avoiding conditional compilation

标签 c++ unit-testing templates mocking integration-testing

我正在尝试测试具有这种结构的具体对象。

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;
  }
};

我不太喜欢编译时真假之间的切换。

所以,我的问题是:

  1. 是否有更好的方法在 Database 和 FakeDatabase 之间切换?例如,是否有可能在运行时以干净的方式完成它?我喜欢避免#ifdefs。
  2. 此外,如果有人有更好的方法来制作模仿具体类的假类,我将不胜感激。

我不想在整个实际测试代码(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/

相关文章:

c++ - 字符串编码问题?

c++ - 如果 C++ stdlib 流的解析编号后紧跟一个非空白字符,如何引发错误?

java - 模拟父类(super class)方法调用

java - Mockito - 奇怪的包范围类继承问题

javascript - 如何使用 Backbone View 在下划线模板中渲染模型属性

c++ - 如何确定将使用哪个模板

c++ - 命令行上的 vcpkg 设置错误

C++ 可变参数模板空包

unit-testing - 开 Jest mock 引用错误

c++ - 模板参数包扩展语法的基本原理