unit testingc ++。 如何testing私人会员?

我想为我的C ++应用程序进行unit testing。

什么是testingclass级私人成员的正确forms? 做一个朋友类,将testing私人会员,使用派生类,或其他一些诡计?

testingAPI使用哪种技术?

通常,只有在问题评论中讨论过的公共接口才会被testing。

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

允许unit testing访问非公共成员的一种方法是通过朋友构造。

回答这个问题触及了许多其他的话题。 除了CleanCode,TDD和其他人的宗教信仰:

有几种访问私人会员的方法。 无论如何,你必须推翻testing的代码! 这在parsingC ++(预处理器和语言本身)的两个层面上是可能的:

定义所有公共

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

 #define private public #define protected public #define class struct 

缺点是, 交付代码的类不同于在testing中 ! 第9.2.13章中的C ++标准说:

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

这意味着,编译器有权对testing的成员variables和虚拟函数进行重新sorting。 如果没有发生缓冲区溢出,您可能会感到困难,这不会对您的类造成危害,但这意味着您将不会testing相同的代码。 这意味着,如果你访问一个对象的成员,这是由代码初始化,编译与private没有定义为public ,成员的偏移可能会有所不同!

这个方法需要改变被testing的类与testing类或testing函数的结合。 一些testing框架如gtest( FRIEND_TEST(..); )具有特殊的function来支持这种访问私人事物的方式。

 class X { private: friend class Test_X; }; 

它只为testing打开类,并不打开世界,但你必须修改交付的代码。 在我看来,这是一件坏事,因为testing不应该改变testing代码。 作为一个进一步的缺点,它使得其他类的交付代码可能通过将自己命名为一个testing类来侵入你的类(这也会损害C ++标准的ODR规则)。

声明受保护的私人事物并从类中派生出testing

不是一个非常优雅的方式,非常侵入,但作品也:

 class X { protected: int myPrivate; }; class Test_X: public X { // Now you can access the myPrivate member. }; 

任何其他方式与macros

工程,但在第一种方式标准符合性有相同的缺点。 例如:

 class X { #ifndef UNITTEST private: #endif }; 

我认为最后的两种方法是前两种方法的替代scheme,因为它们与第一种方法相比没有什么优势,但是在testing代码上更具侵入性。 第一种方式是非常危险的,所以你可以使用友好的方式。


一些关于从不考验私人事物的讨论。 unit testing的一个好处就是,你将会很早就到达这个地步,你必须改进代码的devise。 这有时也是unit testing的缺点之一。 它使得面向对象有时更复杂,而不是必须的。 特别是如果按照规则devise类,就像现实世界中的对象一样。

然后,你必须有时将代码更改为难看的东西,因为unit testing方法迫使你这样做。 用于控制物理过程的复杂框架的工作就是一个例子。 在那里你要映射物理过程的代码,因为过程的部分过程已经非常复杂了。 那个进程的依赖列表有时会很长。 这是一个可能的时刻,testing私人会员变得更好。 你必须权衡每种方法的优缺点。

课程有时变得复杂! 那么你必须决定分裂它们,或者把它们照原样。 有时第二个决定更有意义。 最后总是一个你想达到的目标(如完美的devise,快速的合并时间,低开发成本等等)的问题。


我的想法

我访问私人会员的决定过程如下所示:

  1. 你需要自己testing私人成员吗? (通常这会减less所需的testing总数)
  2. 如果是的话,你有没有看到任何devise优势来重构课堂?
  3. 如果不是,请在class上接受考试(因为缺less备选scheme,请使用此考试)。

我不喜欢这种友好的方式,因为它改变了testing代码,但是testing某些东西的风险可能与第一种方法不尽相同(尽可能采用第一种方法),但这并不能certificate代码更加清晰。

顺便说一句:只testing公共接口也是一个stream利的问题,因为根据我的经验,它随着私有实现的变化而变化。 所以你们没有优势来减less对公众成员的考验。

我自己还没有find一个黄金解决scheme,但是如果你知道testing框架是如何命名它的方法的,你可以使用friend来testing私有成员。 我使用以下来testing私人成员与谷歌testing。 虽然这工作得很好,请注意,这是一个黑客攻击,我不使用它在生产代码。

在我想testing的代码的头文件(stylesheet.h)中,我有:

 #ifndef TEST_FRIENDS #define TEST_FRIENDS #endif class Stylesheet { TEST_FRIENDS; public: // ... private: // ... }; 

在testing中我有:

 #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. } 

如果您添加一个访问私有成员的新testing,则总是添加一个新行到TEST_FRIENDS。 这种技术的好处是,在testing代码中它是相当不显眼的,因为你只需要添加一些#define,这在没有testing的时候是没有效果的。 缺点是在testing中有点冗长。

现在有一个词,你为什么要这样做。 当然,理想的情况是,你有一些职责明确的小class,class上有很容易testing的界面。 但是,在实践中,这并不总是容易的。 如果你正在编写一个图书馆,什么是privatepublic是由你希望图书馆的消费者能够使用的(你的公共API)决定的,而不是由什么需要testing或不需要。 您可以拥有不太可能改变的不variables,并且需要进行testing,但对API的使用者不感兴趣。 然后,API的黑盒testing是不够的。 另外,如果您遇到错误并编写额外的testing以防止回归,则可能需要testingprivate内容。

testing私人成员的愿望是一种devise的气味,通常表明在你的课堂里有一class人被困在外面。 一个class级的所有function都应该通过公共的方法来行使。 无法公开访问的function实际上并不存在。

有几种方法可以实现,你需要testing你的私有方法是否能够做到他们所说的话。 朋友class是最糟糕的; 他们把testing与performance脆弱的方式联系起来。 更好的是dependency injection:使私有方法的依赖类属性,testing可以提供模拟版本,以便允许通过公共接口testing私有方法。 最好的办法是提取一个封装了私有方法作为公共接口的行为的类,然后像平常一样testing新的类。

欲了解更多详情,请参阅清洁代码 。

尽pipe有关于testing私有方法的适当性的评论,但是假设你确实需要……例如,在使用遗留代码之前将其重构为更适当的东西时,通常是这种情况。 这是我用过的模式:

 // In testable.hpp: #if defined UNIT_TESTING # define ACCESSIBLE_FROM_TESTS : public # define CONCRETE virtual #else # define ACCESSIBLE_FROM_TESTS # define CONCRETE #endif 

然后,在代码中:

 #include "testable.hpp" class MyClass { ... private ACCESSIBLE_FROM_TESTS: int someTestablePrivateMethod(int param); private: // Stuff we don't want the unit tests to see... int someNonTestablePrivateMethod(); class Impl; boost::scoped_ptr<Impl> _impl; } 

比定义testing朋友好吗? 它看起来不像其他选项那样冗长,而且在标题内清楚的发生了什么。 这两个解决scheme都与安全无关:如果您真的关心方法或成员,那么这些方法需要隐藏在不透明的实现中,可能还有其他的保护。

使用#define在C ++中有一个简单的解决scheme。 只需包装你的“ClassUnderTest”就可以了:

 #define protected public #define private public #include <ClassUnderTest.hpp> #undef protected #undef private 

[转载到本文和RonFox] [1]

我希望在unit testing的Makefile中添加-Dprivate = public选项,避免修改原始项目中的任何内容