私人方法的单元测试

Mum*_*les 93 c++ unit-testing private-methods

我正在编写一些单元测试.特别是我想测试一些私有方法.

到目前为止,我已经提出了使用.

#define private public
Run Code Online (Sandbox Code Playgroud)

但我对此并不满意,因为它会从单元测试的角度破坏所有封装.

您使用什么方法对私有方法进行单元测试.

Ant*_*ams 73

而不是#define你在问题中提到的讨厌的黑客,一个更清洁的机制是使测试成为被测试类的朋友.这允许测试代码(以及测试代码)访问私有部分,同时保护它们免受其他一切的影响.

但是,最好通过公共接口进行测试.如果你的类X在私有成员函数中有很多代码,那么可能值得提取一个由类X的实现使用的新类Y.然后可以通过它的公共接口测试这个新的类Y,而不暴露它用于X类的客户.

  • 应该做的是使用编译器定义进行单元测试和#ifdef UNIT_TESTING friend void UnitTestFcn()#endif (2认同)

jls*_*ker 61

如果您使用的是Google Test,则可以使用FRIEND_TEST轻松将您的测试夹具声明为受测试类的朋友.

而且你知道,如果像其他一些答案所说的那样测试私有函数是非常糟糕的,那么它可能不会被内置到Google Test中.

您可以在此答案中阅读有关何时测试私有函数的好坏的详细信息.

  • 显然,这是[上诉到权威](https://yourologicfallacyis.com/appeal-to-authority)的一个实例,但是... Google测试是什么权威!最佳答案。 (2认同)
  • 您链接到的Google测试文档强烈建议您重新设计测试专用方法。 (2认同)

Mik*_*our 58

如果方法足够复杂以保证单独进行测试,则将它们重构为自己的类并通过其公共接口进行测试.然后在原始课堂中私下使用它们.

  • 是吧?为什么公共职能部门应该比私人部门更多地进行测试? (63认同)
  • 我不明白为什么要单元测试私有方法是设计气味.让我们说,我开始编写一个类,同时编写单元测试,在开始编写公共函数之前,我编写了一些相当简单的私有函数,我想在开始实现公共函数之前证明它是有效的. (24认同)
  • @Assaf:私有函数不能不受干扰地进行测试,因此如果它们需要单独进行测试,则必须将它们公开(它们在哪里,或在另一个类中),或者添加一个侵入式测试机制.我的建议是重构,以便所有代码都可以不受干扰地进行测试; 在我看来(并非每个人都会分享),这也通过减少原始类的职责,并允许重用以前的私有功能,提供更清晰的设计.如果你喜欢有多重职责的大班,请使用Anthony对朋友测试课的建议. (8认同)
  • @David:这是一种气味,因为它表明该类有多个职责(实现其公共接口,并实现私有功能).将私有功能委托给其他类而不是私有方法可能是一种更简洁的设计. (8认同)
  • @Assaf:从另一个角度来看,我认为如果你改变类的实现细节而不改变它的公共接口(或者如果你愿意的话,保护它),则不需要删除测试.您可能希望在重写之后添加*测试,当您进行白盒测试时,可以捕获新的"冒险"案例.你不应该删除它们 - 新的实现应该做旧的实现的每一个重要的事情.但私有函数是实现细节.当实现细节发生变化时停止工作的测试,即使实际上没有任何内容被破坏,也是令人讨厌的. (6认同)
  • 是的,单元测试私人方法的冲动通常是设计气味. (3认同)
  • @David:在你的特殊情况下,我称之为ad-hoc程序员测试.将测试写为类的一部分(可能是一个返回true或false的公共`test_internals()`函数.也许是一个测试类,它是你的类的"朋友"),或者使函数暂时公开.测试你的私人功能.然后编写您的公共函数,确保它们通过所有测试.完成后,您不再需要或希望您的测试确保您的类包含特定的实现细节,因此请不要检查这些测试. (2认同)
  • 你应该理想地对所有函数进行单元测试...(ala Haskell的QuickCheck)如果你从不对私有方法进行单元测试,你怎么能相信它们在你自己的类中是正确的? (2认同)

Man*_*j R 30

使测试类成为原始类的朋友.这个朋友的声明将在#define UNIT_TEST旗帜内.

class To_test_class {
   #ifdef UNIT_TEST
     friend test_class;
   #endif
}
Run Code Online (Sandbox Code Playgroud)

现在进行单元测试,您将使用flag编译代码-DUNIT_TEST.这样您就可以测试私有函数了.

现在,您的单元测试代码将不会被推送到生产环境中,因为UNIT_TEST标志将为false.因此代码仍然是安全的.

您也不需要任何特殊的库来进行单元测试.


小智 15

我知道这是一个较老的问题,但似乎没有人分享我喜欢的相对好的方法,所以在这里:

将要测试的方法更改privateprotected.对于其他类,该方法仍然是private,但现在您可以从基类派生一个"测试"类,该类公开您要测试的私有功能.

这是一个最小的例子:

class BASE_CLASS {
  protected:
    int your_method(int a, int b);
};

class TEST_CLASS : public BASE_CLASS {
  public:
    int your_method(int a, int b) {
      return BASE_CLASS::your_method(a, b);
    }
}
Run Code Online (Sandbox Code Playgroud)

当然,您必须更新单元测试以在派生类而不是基类上运行测试,但在此之后,对基类所做的任何更改都将自动反映在"testing"类中.

  • 将private更改为protected可以修改类的封装。如果您将测试装置作为朋友,它将获得所需的访问权限,但没有其他方法。 (2认同)

Par*_*ari 8

经过许多小时,对于那些想要测试其私有功能的人来说,这就是我决定的最佳解决方案。这是Max DeLisoMiloš的答案的组合。

如果您使用boost :: unit-test,那么有一个简单而优雅的解决方案。

  1. 在课堂上 使用protected代替private

    /* MyClass.hpp */
    
    class MyClass {
    
    protected:
        int test() {
            return 1;
        }
    };
    
    Run Code Online (Sandbox Code Playgroud)
  2. 创建一个灯具

    /* TestMyClass.cpp */
    
    class F : public MyClass {};
    
    
    BOOST_FIXTURE_TEST_SUITE(SomeTests, F)
    
    // use any protected methods inside your tests
    BOOST_AUTO_TEST_CASE(init_test)
    {
        BOOST_CHECK_EQUAL( test(), 1 );
    }
    BOOST_AUTO_TEST_SUITE_END()
    
    Run Code Online (Sandbox Code Playgroud)

这样,您可以自由使用任何MyClass功能,而无需在班级中#define private public添加好友

  • 它不适用于私人。并且使私有保护仅用于单元测试似乎是荒谬的。 (3认同)

Max*_*iso 5

定义黑客是一个可怕的想法.在编译它时,任意用预处理器重写代码是不明智的.

现在有几个人已经提到过,你是否应该测试私有方法是有争议的.但是,这并未涵盖您故意隐藏构造函数以限制实例化到某些范围或其他一些更深奥的案例的情况.

此外,您不能成为命名空间的朋友,并且"友谊"不会在C++中继承,因此根据您的单元测试框架,您可能遇到麻烦.幸运的是,如果你正在使用Boost.Test,那么就可以通过Fixtures的形式优雅地解决这个问题.

http://www.boost.org/doc/libs/1_52_0/libs/test/doc/html/utf/user-guide/fixture/per-test-case.html

您可以对夹具进行操作,并使其实例化您在单元测试功能中使用的所有实例,将它们声明为夹具的静态实例和模块范围.如果您正在使用命名空间,请不要担心,您可以在命名空间中声明fixture,并在命名空间之外声明测试用例,然后使用范围解析运算符来获取静态成员.

BOOST_FIXTURE_TEST_CASE宏将采取实例化和拆除的夹具你的照顾.