TDD in C++. How to test friend functions of private class?
如果我有一个带有帮助器(私有成员)类的类,就像这样
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | class Obj; class Helper { friend class Obj; private: int m_count; Helper(){ m_count = 0;}; // Note this is a private constructor void incrementCount(){ ++m_count; }; }; class Obj { Helper *m_pHelper; // note that this is a private getter int getHelperCount() { return m_pHelper->m_count; }; // the public API starts here public: Obj() { m_pHelper = new Helper(); }; void incrementCount(){ m_pHelper->incrementCount(); }; }; |
那么我怎么可能TDD这样的系统呢?
1 2 3 | auto obj = new Obj(); obj->incrementCount(); // what to assert??? |
这是我的问题,以下只是一些背景知识。
回答一些答案和评论。
If noone outside the class should be interested, then your tests should not be interested either. – Arne Mertz
If nobody is interested in the value outside the class, why are you – utnapistim
即使外面没有人需要该值,我仍然可能想知道如果它设置正确,因为它被使用该值的类的其他自包含内部方法使用。也许值是控制器用它来更新模型的速度。或者它可能是视图将用它在屏幕上绘制内容的位置。事实上,Obj的所有其他组件都能够访问该变量。这可能是一个糟糕的设计问题,在这种情况下,我想知道我可以有更好的替代方案。该设计列在本文底部的背景部分。
define private public - Marson Mao
喜欢这种巧妙滥用关键词哈哈。但可能还没有被认为是最好的解决方案。
You need to"expose" the friendship relation in the header of your class. Thus you have to acknowledge there the existence of a class used to test yours.
If you use the pImpl idiom, you could make the members of the pImpl itself all public, the pImpl itself private and give your unit tests access to the pImpl - CashCow
这是否意味着我应该在我原来的课堂上为考试做好准备?或者为它添加额外的"测试"方法?
我刚刚开始使用TDD。使用测试类依赖项来侵入原始类是否常见(或更好)?我不认为我有适当的知识来判断。有什么建议吗?
其他:AFAIK TDD不只是编写测试,而是开发过程。我已经读过,我应该只对公共接口编写测试。但问题是,与所讨论的情况一样,大多数代码都包含在私人类中。我如何使用TDD创建这些代码?
背景
仅供参考,如果您想知道我为什么要上私人课程:
我正在开发一个来自cocos2dx的游戏。游戏引擎采用节点树结构进行更新,渲染等,每个游戏对象都将继承引擎中提供的Node类。现在我想在游戏对象上实现MVC模式。因此,对于每个对象,我基本上创建了一个Object类,其中包含3个辅助类,这些类对应于名为ObjectModel,ObjectView,ObjectController的每个MVC组件。从理论上讲,没有人应该直接访问MVC类,只能通过Object类以某种方式访问??它,所以我将其中的3个私有化。将MVC组件明确地作为类的原因是因为视图和控制器以不同的速率更新(更具体地,控制器执行依赖于帧的更新,而视图基于模型数据执行简单的插值)。 Model类是纯粹出于宗教原因而创建的lol。
提前致谢。
How to test friend functions of private class?
汝不应!
类(或模块或库或其他)出于某种原因公开公共接口。你有公共接口(适合客户端使用,因此它具有不变量,前置条件,后置条件,副作用,无论什么 - 可以并且应该测试)和实现细节,使您可以更轻松地实现公共接口。
拥有私有实现的关键是,您可以随意更改它,而不会影响其他代码(甚至不会影响测试)。更改私有实现后,所有测试都应该通过,客户端(和测试)代码应该(根据设计)完全不关心您更改了私有实现。
So how may I TDD such a system?
TDD仅限您的公共接口。测试实现细节意味着您最终编码到实现,而不是接口。
关于你的评论:
The problem is I don't even have a getter in the public interface. So how can my test check that the value is 0 or 1? And the getter is intentionally made private as no one should be interested in the value outside the class
如果没有人对课外的价值感兴趣,你为什么要这样做(也就是为什么你想测试它呢?)
#define私有公共技巧可能会产生副作用,因为某些编译器会修改函数符号(Visual c ++编译器在其名称中包含访问说明符)
您还可以使用using语句更改可见性:
1 2 3 4 | struct ObjTest : public Obj { using Obj::incrementCount; } |
但就像其他人说的那样,尽量不要测试私人物品。
正如@Marson Mao所说,我投票给#define私人公众。
如果你想更多地控制私有或公开的内容,可以在yourtests.cpp中执行此操作
1 2 3 4 | #define private public #include"IWantAccessViolationForThis.h" #undef private #include"NormalFile.h" |
通过这种方式,您可以获得更多控制权并尝试在尽可能少的地方执行此操作。
这种方法的另一个不错的特性是它是非侵入性的,这意味着您不需要使用#ifdefs来混淆真实的实现和头文件以进行测试而不是测试模式。
我在编写单元测试时也遇到过这样的问题。
经过一番搜索,我认为最有效的方法是在
1 | #define private public |
注意:在您想要的包含文件之前添加它,例如,您的
我认为这种方法看起来很疯狂,但它实际上是合理的,因为这个
一些参考:
私人方法的单元测试
您的朋友可以完全访问其所属的班级。这可能有很多原因,其中一个可能是单元测试目的,即你希望能够编写一个单元测试,可以调用类的私有成员,并检查内部变量显示你期望他们显示,但你不希望它成为公共API的一部分。
你需要在你班级的标题中"揭露"友谊关系。因此,你必须承认存在一个用来测试你的类。不用担心,你在现实世界中发展并且课程都经过测试。
为了编写单元测试,您需要实现该类以提供调用所有相关私有函数或获取私有成员的受保护成员函数(可能是静态函数),然后编写派生自您的成员函数。请注意,那些不会直接访问,因为友谊不会被继承,因此静态受保护的成员。
如果你使用pImpl惯用法,你可以使pImpl本身的成员全部公开,pImpl本身是私有的,并让你的单元测试访问pImpl(通过与上面相同的模型)。这现在更简单,因为您只需要为"测试人员"创建一个方法。
关于类的数据成员,近年来我已知将所有这些都放入结构中,即将它们全部公开,然后让类具有该结构的私有实例。处理这类事情,以及序列化/工厂到你的类可以更容易,在那里他们可以创建全部公开的结构,然后从中构建你的类。