C++ 创建可测试代码

C++ 创建可测试代码,c++,unit-testing,refactoring,testability,C++,Unit Testing,Refactoring,Testability,我有一个文件——在一个大型的遗留代码库中——包含访问数据库的方法。 不使用任何类,只使用带有方法声明的头文件和带有实现的源文件 我想覆盖这些方法,以消除单元测试期间的数据库访问。 我想到了以下选项: 将文件设为类并重写方法。 这里的主要缺点是它会导致整个代码库中的许多更改。 不理想,尽管它确实改进了代码 用#ifdef PRODUCTION_code包装整个源文件,并创建一个包含存根的新源文件,并用相反的方式包装它,即使整个内容依赖于编译。这里的问题是,在执行回归测试的构建系统中,我必须编译两次

我有一个文件——在一个大型的遗留代码库中——包含访问数据库的方法。 不使用任何类,只使用带有方法声明的头文件和带有实现的源文件

我想覆盖这些方法,以消除单元测试期间的数据库访问。

我想到了以下选项:

  • 将文件设为类并重写方法。
    这里的主要缺点是它会导致整个代码库中的许多更改。
    不理想,尽管它确实改进了代码
  • #ifdef PRODUCTION_code
    包装整个源文件,并创建一个包含存根的新源文件,并用相反的方式包装它,即使整个内容依赖于编译。这里的问题是,在执行回归测试的构建系统中,我必须编译两次,一次是为了创建应用程序和进行回归测试,另一次是为了创建单元测试可执行文件。
    有什么推荐的方法吗?

    使用现有函数,将代码移到一个新类中,并从现有函数调用新方法,然后在测试期间重写该类如何

    像这样:

     static DBAccessClass dac = new DBAccessClass ();
    
     void origFunction() { dac.origFunction(); }
    
    在测试中:

     dac = new DBAccessMockup();
    

    您可以尝试在链接器级别重写。有两个不同的.cpp文件,它们在描述DB接口的头文件中实现函数——一个调用真实DB,一个调用假接口。当您链接进行单元测试时,请将其中一个替换为另一个(GNU make中的目标特定变量可能会有所帮助)。

    您可能还想看看Michael Feathers的书。他不仅讨论了这些类型的问题,而且书中包含了许多C++中的例子(除了java、C和C)。羽毛也是羽毛的最初创造者

  • 将文件设为类并重写方法。[……]
  • 用#ifdef[…]包装整个源文件
  • 如果可能,使用1,因为您将有一种集中的方式来引用数据库代码(类指针而不是X函数)。这意味着模块化、易于替换实现(使用存根或另一个DB后端)和更多封装的代码

    如果您使用2,请考虑替换函数的实现。也就是说,在原始功能内,启用测试代码(基于

    if


    无论是否在测试环境中运行,您的测试代码都是完全不可知的,性能损失可以忽略不计(
    if(booleanfaghere)
    在大多数情况下成本可以忽略不计),并且您根本不必修改测试代码。

    实际上#2不需要两次编译,因为单元测试是一个单独的模块。是的,或多或少的“pimpl”,也是一个合理的选择。它的最大优点是您只需要更改一段代码,甚至可以为DB方法编写测试。