单元测试c ++.如何测试私人会员?

Dan*_*aad 38 c++ testing unit-testing

我想为我的C++应用程序进行单元测试.

测试班级私人成员的正确形式是什么?创建一个测试私有成员,使用派生类或其他技巧的朋友类?

测试API使用哪种技术?

Mr *_*ooz 37

通常,只会测试公共接口,如问题评论中所述.

但有时候测试私有或受保护的方法会有所帮助.例如,实现可能具有一些对用户隐藏的非平凡复杂性,并且可以通过访问非公共成员来更精确地测试.通常最好找出一种方法来消除这种复杂性,或者弄清楚如何公开公开相关部分,但并非总是如此.

允许单元测试访问非公共成员的一种方法是通过friend结构.

  • 是的,我对这个网站上的众多人感到困惑,他们明确表示不仅仅是好的,而是_desirable_编写未经过直接测试的代码 - 即私人成员.有时候,使用非平凡的私有函数没有好办法,有时候通过调用公共接口很难测试这些私有函数中的所有边缘情况.单元测试的真正目的是使代码更好_,而不是区分公共和私有接口的一些废话.在这种情况下,不测试私人成员是不专业的. (19认同)
  • +1最后一个答案没有说"不测试私人方法".即使糟糕的工程师不这么认为,包容并不总是有效. (16认同)
  • 在某些领域,如安全关键工业设备和医疗设备开发法规,迫使您广泛测试私有和受保护的成员功能……尽管从功能的角度来看这样做是否有意义。 (2认同)

Ste*_*ser 22

回答这个问题涉及许多其他主题.除了CleanCode,TDD和其他人的任何宗教信仰:

有几种方法可以访问私有成员.无论如何,你必须否决测试的代码!这在解析C++(预处理器和语言本身)的两个级别都是可能的:

全部定义为公开

通过使用预处理器,您可以打破封装.

#define private public
#define protected public
#define class struct
Run Code Online (Sandbox Code Playgroud)

缺点是,交付代码的类与测试中的类不同!第9.2.13章中的C++标准说:

未指定具有不同访问控制的非静态数据成员的分配顺序.

这意味着,编译器有权为测试重新排序成员变量和虚函数.如果没有缓冲区溢出发生,这可能会对您的类造成损害,但这意味着您不会测试与传递相同的代码.这意味着,如果你访问一个对象的成员,这是由初始化代码,编译private没有定义的public,您的会员可以不同的偏移!

此方法需要更改测试类以使用测试类或测试函数与其进行交互.像gtest(FRIEND_TEST(..);)这样的测试框架具有支持这种访问私有事物的特殊功能.

class X
{
private:
    friend class Test_X;
};
Run Code Online (Sandbox Code Playgroud)

它仅为测试打开类,不会打开世界,但您必须修改传递的代码.在我看来,这是一件坏事,因为测试永远不会改变测试代码.作为另一个缺点,它为其他类提供的代码提供了通过将自己命名为测试类来侵入您的类的可能性(这也会损害C++标准的ODR规则).

声明保护的私有物品并从类中派生出来进行测试

不是一个非常优雅的方式,非常侵入性,但也有效:

class X
{
protected:
    int myPrivate;
};

class Test_X: public X
{
    // Now you can access the myPrivate member.
};
Run Code Online (Sandbox Code Playgroud)

用宏的任何其他方式

有效,但在标准符合性方面具有与第一种方式相同的缺点.例如:

class X
{
#ifndef UNITTEST
private:
#endif
};
Run Code Online (Sandbox Code Playgroud)

我认为最后两种方式都不能替代前两种方式,因为它们与第一种方式相比没有优势,但对测试代码更具侵入性.第一种方式是非常危险的,所以你可以使用友好的方法.


关于永不测试私密事物讨论的一些话.单元测试的一个优点是,您将很早就达到这一点,您必须改进代码的设计.这有时也是单元测试的缺点之一.它使得面向对象有时比它必须更加复杂.特别是如果您按照规则设计类,就像现实世界对象一样.

然后你必须将代码更改为丑陋的东西,因为单元测试方法迫使你这样做.处理用于控制物理过程的复杂框架就是一个例子.在那里,您希望在物理过程中映射代码,因为过程的某些部分通常非常复杂.该进程的依赖列表有时会很长.这是一个可能的时刻,测试私人成员变得越来越好.您必须权衡每种方法的优缺点.

课程有时变得复杂!然后你必须决定拆分它们或按原样取下它们.有时第二个决定更有意义.最后,问题始终是您想要实现的目标(例如,完美的设计,快速的合并时间,低开发成本......).


我的看法

我访问私有成员的决策过程如下所示:

  1. 你需要自己测试私人会员吗?(这通常会减少所需的测试总数)
  2. 如果是的话,你认为重构课程有什么设计优势吗?
  3. 如果不是,请在您的课堂上与测试结果(因为缺少替代品而使用此测试).

我不喜欢这种友好的方法,因为它改变了测试的代码,但测试某些东西的风险可能与交付的风险不同(尽可能使用第一种方法),并不能证明更干净的代码.

顺便说一句:只测试公共接口也是一个流畅的问题,因为根据我的经验,它会像私有实现那样频繁地改变.因此,您无权减少对公共成员的测试.


jdm*_*jdm 19

我自己没有找到一个黄金解决方案,但friend如果您知道测试框架如何命名它的方法,您可以使用它来测试私有成员.我使用以下内容通过Google测试来测试私人成员.虽然这很有效,但请注意它是一个黑客,我不会在生产代码中使用它.

在我要测试的代码的标题(stylesheet.h)中,我有:

#ifndef TEST_FRIENDS
#define TEST_FRIENDS
#endif

class Stylesheet {
TEST_FRIENDS;
public:
    // ...
private:
    // ...
};
Run Code Online (Sandbox Code Playgroud)

在测试中我有:

#include <gtest/gtest.h>

#define TEST_FRIENDS \
    friend class StylesheetTest_ParseSingleClause_Test; \
    friend class StylesheetTest_ParseMultipleClauses_Test;

#include "stylesheet.h"

TEST(StylesheetTest, ParseSingleClause) {
    // can use private members of class Stylesheet here.
}
Run Code Online (Sandbox Code Playgroud)

如果添加访问私有成员的新测试,则始终向TEST_FRIENDS添加新行.这种技术的好处在于它在测试代码中相当不引人注意,因为你只添加了几个#defines,它们在不测试时没有效果.缺点是它在测试中有点冗长.

现在用一个词来说明你为什么要这样做.当然,理想情况下,您可以使用定义明确的小类,并且类具有易于测试的接口.然而,在实践中并不总是那么容易.如果你正在写一个库,是什么privatepublic被你想要的库的消费者能够使用(你的公共API)决定的,而不是由什么是需要的测试与否.您可以使用不太可能更改的不变量,并且需要进行测试,但API的使用者不感兴趣.然后,API的黑盒测试是不够的.此外,如果您遇到错误并编写其他测试以防止回归,则可能需要测试private内容.

  • 我知道这已经过时了,但我只是在使用gtest和搞乱调用私人成员,并希望更新gtest的建议:[测试私人成员](https://github.com/google/googletest/blob /master/googletest/docs/AdvancedGuide.md#private-class-members).最低限度,您现在可以使用`FRIEND_TEST(TestCaseName,TestName)`将测试声明为朋友,而不是依赖于命名约定.使其安全生产!(必须"#include"gtest/gtest_prod.h""). (2认同)

归档时间:

查看次数:

28929 次

最近记录:

7 年,10 月 前