我有一个单元测试班Tester
;我希望它访问Working
类的私有字段。
class Working {
// ...
private:
int m_variable;
};
class Tester {
void testVariable() {
Working w;
test( w.m_variable );
}
}
我有以下选择:
public
- 丑陋test_getVariable()
- 过度复杂的friend class Tester
添加到工作中 - 然后明确地工作“知道”测试人员,这不是很好我的理想是
class Working {
// ...
private:
int m_variable;
friend class TestBase;
};
class TestBase {};
class Tester : public TestBase {
void testVariable() {
Working w;
test( w.m_variable );
}
}
在哪里工作了解TestBase而不是每个测试...但它不起作用。显然友谊不适用于继承。
这里最优雅的解决方案是什么?
答案 0 :(得分:16)
通常,您的单元测试不应评估私有变量。将测试写入接口,而不是实现。
如果您确实需要检查私有变量是否具有特定特征,请考虑使用assert()
而不是尝试为其编写单元测试。
更长的答案(为C#而不是C ++编写,但适用相同的原则)在https://stackoverflow.com/a/1093481/436641。
答案 1 :(得分:14)
我同意Trott's answer,但有时您会将单元测试添加到非为其设计的遗留代码中。在这些情况下,我会到达#define private public
。它只是在单元测试中,而且只是因为重构过于昂贵而无法理解。它很丑陋,技术上非法,非常有效。
答案 2 :(得分:4)
-fno-access-control
如果您只使用GCC,则可以在编译单元测试时使用编译器选项-fno-access-control
。这将导致GCC跳过所有访问检查,但仍保持类布局相同。我不知道其他编译器是否有类似的选项,所以这不是一般的解决方案。
答案 3 :(得分:3)
尽量使用公共界面测试所有私有代码。最初它不仅工作量减少,而且当更改实现时,单元测试的可能性仍然高得多。
那就是说,有时候你只需要戳内脏来获得良好的测试覆盖率。在这种情况下,我使用一个成语,我称之为公开。如果你考虑一下就会有一个笑话。
需要测试的Foo类
class Foo
{
public:
// everyone is on their honor to only use Test for unit testing.
// Technically someone could use this for other purposes, but if you have
// coders purposely doing bad thing you have bigger problems.
class Test;
void baz( void );
private:
int m_int;
void bar( void );
};
foo_exposed.h仅适用于单元测试代码。
class Foo::Test : public Foo
{
public:
// NOTE baz isn't listed
// also note that I don't need to duplicate the
// types / signatures of the private data. I just
// need to use the name which is fairly minimal.
// When i do this I don't list every private variable here.
// I only add them as I need them in an actual unit test, YAGNI.
using Foo::m_int;
using Foo::bar;
};
// yes I'm purposely const smashing here.
// The truth is sometimes you need to get around const
// just like you need to get around private
inline Foo::Test& expose( const Foo& foo )
{
return * reinterpret_cast<Foo::Test*>(
&const_cast<Foo::Test&>( foo )
);
}
如何在单元测试代码中使用
#include "foo_exposed.hpp"
void test_case()
{
const Foo foo;
// dangerous as hell, but this is a unit test, we do crazy things
expose(foo).m_int = 20;
expose(foo).baz();
}
答案 4 :(得分:2)
如果您绝对必须这样做,您可以有条件地编译您的代码,以便TestBase仅在单元测试时才是朋友:
class Working {
// ...
private:
int m_variable;
#ifdef UNIT_TESTING
friend class TestBase;
#endif
};
答案 5 :(得分:0)
我是通过在我的测试中使用我的类头文件的副本来完成此操作,该文件缺少“私有”访问说明符。该副本由测试目录中的makefile生成,以便在原始更改时重新生成副本:
perl -ne 'print unless m/private:/;' < ../include/class_header.h > mock_class_header.h
和'test'make目标取决于mock_class_header.h。
这允许访问测试中的所有私有成员变量,即使真正的库是使用这些成员变量私有而编译的。