C++ 私有成员数据的模拟
C++ 私有成员数据的模拟,c++,unit-testing,C++,Unit Testing,我在寻找C++编译风格的最佳实践,以确保单元测试的容易性。问题来自于试图为私有数据成员实现一个模拟类。私有数据成员通过类中的几个不同方法进行访问。到目前为止,我所能找到的所有示例都展示了如何编写模拟类,但没有展示如何最好地编写同时使用真实对象和模拟对象的代码 在下面的示例中,我不确定如何将mCustom从类型MyOtherClass转换为mock MockMyOtherClass。我怀疑我的方法是错误的,这就是问题所在 class MyClass { MyOtherClass mCust
我在寻找C++编译风格的最佳实践,以确保单元测试的容易性。问题来自于试图为私有数据成员实现一个模拟类。私有数据成员通过类中的几个不同方法进行访问。到目前为止,我所能找到的所有示例都展示了如何编写模拟类,但没有展示如何最好地编写同时使用真实对象和模拟对象的代码 在下面的示例中,我不确定如何将mCustom从类型MyOtherClass转换为mock MockMyOtherClass。我怀疑我的方法是错误的,这就是问题所在
class MyClass {
MyOtherClass mCustom;
};
[编辑]我使用了一个编译器指令和一个新的构造函数
#ifdef UNIT_TESTING
#include "mock.h"
#else
#include "myotherclass.h"
#endif
class MyClass {
MyOtherClass mCustom;
public:
MyClass(MyOtherClass pClass) : mCustom(pClass) {}
};
当我以前遇到这个问题时,我将
MyClass
分为两类:(1)不引用MyOtherClass
的泛型类,以及(2)使用MyOtherClass
实例化模板参数的实现类。定义的可见性并不完全相同,但我发现它对于任何实际目的都不重要
然后为了测试,我编写了类似于
MyTestOtherClass
的东西,并相应地实例化了MyClass
。任何数量的模拟技术都可以应用于MyTestOtherClass,并且可以为不同的测试目的生成多个类。您可以使用一些不同的策略(并根据需要进行混合)
class MyClass {
private:
MyOtherClass mCustom;
public:
//...
};
template <class T>
class MyClass_impl {
private:
T mCustom;
public:
//...
};
typedef MyClass_impl<MyOtherClass> MyClass; // convenience typedef
假设正在使用
#ifndef
头保护,一种不需要任何代码重新架构的黑客方法是利用它们,在包含实际头之前在测试代码中定义头保护宏
例如:
MyClass.hpp:
#ifndef MY_CLASS_HPP
#define MY_CLASS_HPP
#include "MyOtherClass.hpp"
class MyClass { MyOtherClass custom; };
#endif
#ifndef MY_OTHER_CLASS_HPP
#define MY_OTHER_CLASS_HPP
class MyOtherClass {};
#endif
MyOtherClass.hpp:
#ifndef MY_CLASS_HPP
#define MY_CLASS_HPP
#include "MyOtherClass.hpp"
class MyClass { MyOtherClass custom; };
#endif
#ifndef MY_OTHER_CLASS_HPP
#define MY_OTHER_CLASS_HPP
class MyOtherClass {};
#endif
TestCode.cpp:
class MyOtherClass {
// mocking
};
#define MY_OTHER_CLASS_HPP
// prevents the #include "MyOtherClass.hpp" in "MyClass.hpp" from doing anything
// while the mock version is used
#include "MyClass.hpp"
// test code
如果您试图测试的代码位于.cpp文件中,您也可以在测试中包含.cpp文件(确保不要单独编译以避免重复定义),以便插入header guard宏定义。这些都是可行的解决方案。然而,如果你选择第4个,你将被其他开发者烧死——这可能就是为什么它在这个列表中最后一个:)就个人而言,我是方法1的粉丝,认为它是侵入性最小的。在我的例子中,“MyOtherClass”与一个设备驱动程序对话,所以我需要把它从循环中拿出来进行测试。所以选项1不是一个选项。其他的看起来很有趣。@Matt:Option 4实际上已经被我和其他开发者在一个商业项目中使用过了。不要冒险,只使用实用的解决方案。@Jim:选项2确实不允许有效地分离接口和类的实现。@Bartvaningeschenau好吧,这是真的:可以做到。它在某种程度上也很容易出错,这可能会花费人们大量的时间:在代码中没有任何提示的情况下交换类,并且您必须仔细考虑构建过程中的一行,以寻找不同的目录顺序-远离源代码,而您通常会将其视为开发人员。非常危险。但这是可以做到的。