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,快速的合并时间,低开发成本等等)的问题。
我的想法
我访问私人会员的决定过程如下所示:
- 你需要自己testing私人成员吗? (通常这会减less所需的testing总数)
- 如果是的话,你有没有看到任何devise优势来重构课堂?
- 如果不是,请在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的界面。 但是,在实践中,这并不总是容易的。 如果你正在编写一个图书馆,什么是private
和public
是由你希望图书馆的消费者能够使用的(你的公共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选项,避免修改原始项目中的任何内容
- 在Rails中testing时如何设置HTTP_REFERER?
- 什么是FLOP / s,这是一个很好的衡量performance?
- Assert.AreNotEqual和Assert.AreNotSame有什么区别?
- unit testing是否可以成功添加到现有的生产项目中? 如果是这样,那么值得吗?
- 有没有Java的假文件系统框架?
- Django TestCase的Assertion Methods列表在哪里?
- TDD和Test First Development(或testing优先编程)之间有区别吗?
- 是否有可能在轨道集成testing或规范中指定用户代理?
- 集成和unit testing有什么区别?