【问题标题】:Unit testing a class constructor对类构造函数进行单元测试
【发布时间】:2012-11-19 05:21:19
【问题描述】:

在 C++ 中,如何测试类构造函数?例如:

Class myClass{
    int a;

public:
    myClass(); //Constructor
    void reset(); //resets a to 0;

};

//Constructor defined to initialize a to 0
myClass::myClass(){
    reset();
}

//reset sets a to 0
void myClass::reset(){
    a = 0;
}

如何测试 myClass() 构造函数?我无法访问私有数据成员,所以说如果重置有效则构造函数有效吗?

【问题讨论】:

    标签: c++ unit-testing class testing


    【解决方案1】:

    我将把你的问题改写为:我如何测试我的类的属性,因为它们是受保护的或私有的,所以不能从我的测试中直接观察到?以下是我通常认为的几种可能性:

    • 使用 assert() 将测试的检查部分移动到类中。如果您要测试的是您的构造函数已成功建立一些复杂的不变量,那么您可以在验证该不变量的构造函数的末尾放置一个 assert() 。然后,您的测试所要做的就是调用构造函数——构造函数中的断言会执行测试本来会执行的实际检查。如果您在编写代码时习惯性地添加断言,那么该断言已经存在(如果断言使您的调试构建太慢,请使用分析器来查明那些实际上存在问题的断言)。

    • 提取您要测试的复杂代码段到第二个类中。然后,您可以通过其公共接口测试第二个类。即使不考虑可测试性,在提取第二类改进代码时,此选项也很有吸引力。

    • 更改您的代码设计,以其他方式更易于测试。同样,即使不考虑可测试性,当更改是改进时,这也很有吸引力。

    • 战略撤退。在其他条件相同的情况下单独测试代码是好的,但没有法律规定所有代码必须 em> 单独测试。有时正确的答案是让代码被测试作为测试其他东西的副作用。毕竟,您对该类的所有其他测试都将调用构造函数,因此,如果构造函数出现问题,则可能会导致其他测试之一失败。以这种方式覆盖所有路径可能会更加困难,并且在您遇到故障时,追踪故障原因将更加麻烦,因为现在您不知道问题是构造函数还是正在测试的其他事物.这可能是也可能不是什么大问题。考虑到这段时间你还可以使用什么以及这个测试实际上会发现错误的可能性,单独测试这段代码真的值得吗?代码审查会是一种更有用的方式来花费这些时间吗?还有其他更有用的事情吗?

    如果您确实必须测试无法用 assert() 测试的东西,并且没有可接受的重构来提高可测试性,那么仍然有一些选择。我从来不需要使用这些来进行测试,但它们在这里:

    • 使用朋友声明让您的测试访问您班级的私有字段。现在每个类都需要为需要访问的测试维护一个友元声明列表。这可能是一件苦差事。

    • 维护一个单独的接口供测试使用。有一组方法可以提供您需要的访问权限,并且其名称具有“testOnly”之类的前缀。因此,在您的情况下,它将是 testOnlyGetA()。您必须遵守纪律,只从测试中调用这些方法,并且您现在需要维护比以前更大的接口。

    • 捏住鼻子并在测试文件中输入#define private public,然后再包含您要测试的标头。

    【讨论】:

    • #define private public 非常hackish。
    • @becko 是的,这就是你必须捂住鼻子的原因 :) 我不建议这样做,但这是一种选择。
    【解决方案2】:

    更新。抱歉,误会了。

    你可以给一个类或者类成员的测试函数添加一个友元测试函数(这更好地测试实现细节,但有时由于测试框架的限制是不可能的)

    【讨论】:

      【解决方案3】:

      你可以在类中添加一个 boost 测试用例:

      Class myClass{
          private:
              int a;
      
          public:
              myClass(); //Constructor
              void reset(); //resets a to 0;
              void test_a_reset() {
                  BOOST_CHECK_EQUAL(a, 0);
              }
      
      };
      

      我不确定您是如何进行测试的。这可能不是你想要的。

      【讨论】:

        【解决方案4】:

        您可以声明一个与原始类具有相同内存布局的测试类,但所有成员都是公共的。

        void testMyClassConstructor()
        {
            class myClassPublic
            {
            public:
                int a;
                myClassPublic();
                void reset();
            };
        
            typedef union
            {
                myClass* original;
                myClassPublic* testView;
            } u_myClass;
        
            myClass testInstance;
            u_myClass testUnion;
            testUnion.original = &testInstance;
        
            assert(testUnion.testView->a == 0);
        }
        

        如果您不能更改测试类的接口,这在白盒测试用例中很好。 但是,请记住,对原始类的内存布局的任何更改也会破坏单元测试。

        【讨论】:

        • 这和#define private public 一样糟糕,而且启动时的可维护性较差。
        猜你喜欢
        • 1970-01-01
        • 2020-12-03
        • 1970-01-01
        • 2010-09-26
        • 2012-06-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-09-04
        相关资源
        最近更新 更多