3

在 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() 构造函数?我无法访问私有数据成员,所以说如果重置有效,那么构造函数有效吗?

4

4 回答 4

9

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

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

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

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

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

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

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

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

  • #define private public在包含您要测试的标头之前,请先在您的测试文件中进行操作

于 2012-11-19T16:31:41.280 回答
0

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

Class myClass{
    private:
        int a;

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

};

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

于 2012-11-19T05:44:03.097 回答
0

更新。对不起,误会了。

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

于 2012-11-19T05:26:50.320 回答
-1

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

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

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

于 2012-11-19T14:44:38.663 回答