如何对 C++ 类的私有成员(和方法)进行单元测试
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/6778496/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to do unit testing on private members (and methods) of C++ classes
提问by Jir
I am very new to unit testing and I am a little confused.
我对单元测试很陌生,我有点困惑。
I am trying to do unit testing (using the Boost unit testing framework) on a C++ class called VariableImpl
. Here are the details.
我正在尝试在名为VariableImpl
. 这是详细信息。
class Variable
{
public:
void UpdateStatistics (void) {
// compute mean based on m_val and update m_mean;
OtherClass::SendData (m_mean);
m_val.clear ();
}
virtual void RecordData (double) = 0;
protected:
std::vector<double> m_val;
private:
double m_mean;
};
class VariableImpl : public Variable
{
public:
virtual void RecordData (double d) {
// put data in m_val
}
};
My question is how can I check that the mean is computed correctly? Note that 1) m_mean
is protected and 2) UpdateStatistics
calls a method of another class and then clears the vector.
我的问题是如何检查平均值计算是否正确?请注意,1)m_mean
受保护和 2)UpdateStatistics
调用另一个类的方法,然后清除向量。
The only way I can see would be to add a getter (for instance, GetMean
), but I don't like this solution at all, nor I think it is the most elegant.
我能看到的唯一方法是添加一个 getter(例如,GetMean
),但我根本不喜欢这个解决方案,我也不认为它是最优雅的。
How should I do?
我应该怎么做?
And what should I do if I were to test a private method instead of a private variable?
如果我要测试私有方法而不是私有变量,我该怎么办?
TIA,
TIA,
Jir
吉尔
回答by Konrad Rudolph
Well, unittesting should test unitsand ideally every class is a self-contained unit – this follows directly from the single responsibility principle.
嗯,单元测试应该测试单元,理想情况下每个类都是一个独立的单元——这直接来自单一职责原则。
So testing private members of a class shouldn't be necessary – the class is a black box that can be covered in a unit test as-is.
所以测试一个类的私有成员应该是不必要的——这个类是一个可以在单元测试中按原样覆盖的黑盒子。
On the other hand, this isn't always true, and sometimes with good reasons (for instance, several methods of the class could rely on a private utility function that should be tested). One very simple, very crufty but ultimately successful solution is to put the following into your unit-test file, beforeincluding the header that defines your class:
另一方面,这并不总是正确的,有时有充分的理由(例如,类的几个方法可能依赖于应该测试的私有实用程序函数)。一个非常简单、非常粗暴但最终成功的解决方案是在包含定义类的头文件之前将以下内容放入单元测试文件中:
#define private public
Of course, this destroys encapsulation and is evil. But for testing, it serves the purpose.
当然,这会破坏封装并且是邪恶的。但是对于测试,它达到了目的。
回答by DumbCoder
For a protected method/variable, inherit a Test class from the class and do your testing.
对于受保护的方法/变量,从类继承 Test 类并进行测试。
For a private, introduce a friend class. It isn't the best of solutions but can do the work for you.
对于私人,介绍一个朋友班。这不是最好的解决方案,但可以为您完成工作。
Or this hack
或者这个黑客
#define private public
回答by mindriot
In general, I agree with what others have said on here - only the public interface should be unit tested. Nevertheless, I've just had a case where I had to call a protected method first, to prepare for a specific test case. I first tried the #define protected public
approach mentioned above; this worked with Linux/gcc, but failed with Windows/VisualStudio. The reason was that changing protected
to public
also changed the mangled symbol name and thus gave me linker errors: the library provided a protected__declspec(dllexport) void Foo::bar()
method, but with the #define
in place, my test program expected a public__declspec(dllimport) void Foo::bar()
method which gave me an unresolved symbol error.
总的来说,我同意其他人在这里所说的 - 只有公共接口应该进行单元测试。尽管如此,我刚刚遇到了一个案例,我必须首先调用一个受保护的方法,为特定的测试案例做准备。我首先尝试了#define protected public
上面提到的方法;这适用于 Linux/gcc,但在 Windows/VisualStudio 中失败。原因是更改protected
为public
也更改了损坏的符号名称,从而给了我链接器错误:该库提供了一个受保护的__declspec(dllexport) void Foo::bar()
方法,但是#define
在适当的位置,我的测试程序需要一个公共__declspec(dllimport) void Foo::bar()
方法,这给了我一个未解决的符号错误。
For this reason I switched to a friend
based solution, doing the following in my class header:
出于这个原因,我切换到friend
基于解决方案,在我的类标题中执行以下操作:
// This goes in Foo.h
namespace unit_test { // Name this anything you like
struct FooTester; // Forward declaration for befriending
}
// Class to be tested
class Foo
{
...
private:
bool somePrivateMethod(int bar);
// Unit test access
friend struct ::unit_test::FooTester;
};
And in my actual test case, I did this:
在我的实际测试用例中,我这样做了:
#include <Foo.h>
#include <boost/test/unit_test.hpp>
namespace unit_test {
// Static wrappers for private/protected methods
struct FooTester
{
static bool somePrivateMethod(Foo& foo, int bar)
{
return foo.somePrivateMethod(bar);
}
};
}
BOOST_AUTO_TEST_SUITE(FooTest);
BOOST_AUTO_TEST_CASE(TestSomePrivateMethod)
{
// Just a silly example
Foo foo;
BOOST_CHECK_EQUAL(unit_test::FooTester::somePrivateMethod(foo, 42), true);
}
BOOST_AUTO_TEST_SUITE_END();
This works with Linux/gcc as well as Windows/VisualStudio.
这适用于 Linux/gcc 以及 Windows/VisualStudio。
回答by photoscar
Good approach to test the protected data in c + + is the assignment of a friend proxy class:
在 c++ 中测试受保护数据的好方法是分配一个朋友代理类:
#define FRIEND_TEST(test_case_name, test_name)\
friend class test_case_name##_##test_name##_Test
class MyClass
{
private:
int MyMethod();
FRIEND_TEST(MyClassTest, MyMethod);
};
class MyClassTest : public testing::Test
{
public:
// ...
void Test1()
{
MyClass obj1;
ASSERT_TRUE(obj1.MyMethod() == 0);
}
void Test2()
{
ASSERT_TRUE(obj2.MyMethod() == 0);
}
MyClass obj2;
};
TEST_F(MyClassTest, PrivateTests)
{
Test1();
Test2();
}
see more goolge test (gtest): http://code.google.com/p/googletest-translations/
查看更多谷歌测试(gtest):http: //code.google.com/p/googletest-translations/
回答by Tom Kerr
Unit test VariableImpl such that if its behavior is ensured, so is Variable.
单元测试 VariableImpl,如果它的行为得到保证,那么 Variable 也是如此。
Testing internals isn't the worst thing in the world, but the goal is that they can be anything as long as the interfaces contracts are ensured. If that means creating a bunch of weird mock implementations to test Variable, then that is reasonable.
测试内部结构并不是世界上最糟糕的事情,但目标是只要保证接口契约,它们就可以是任何东西。如果这意味着创建一堆奇怪的模拟实现来测试变量,那么这是合理的。
If that seems like a lot, consider that implementation inheritance doesn't create great separation of concerns. If it is hard to unit test, then that is a pretty obvious code smell for me.
如果这看起来很多,请考虑实现继承并没有创建很大的关注点分离。如果很难进行单元测试,那么这对我来说是一种非常明显的代码味道。
回答by altamic
While in my opinion the need of testing private members/methods of a class is a code smell, I think that is technically feasible in C++.
虽然在我看来需要测试类的私有成员/方法是一种代码味道,但我认为这在 C++ 中在技术上是可行的。
As an example, suppose you have a Dog class with private members/methods except for the public constructor:
举个例子,假设你有一个 Dog 类,除了公共构造函数之外,还有私有成员/方法:
#include <iostream>
#include <string>
using namespace std;
class Dog {
public:
Dog(string name) { this->name = name; };
private:
string name;
string bark() { return name + ": Woof!"; };
static string Species;
static int Legs() { return 4; };
};
string Dog::Species = "Canis familiaris";
Now for some reason you would like to test the private ones. You could use privablicto achieve that.
现在出于某种原因,您想测试私有的。您可以使用privablic来实现这一点。
Include a header named privablic.halong with the desired implementation like that:
包括一个名为privablic.h的标头以及所需的实现,如下所示:
#include "privablic.h"
#include "dog.hpp"
then map some stubs according to types of any instance member
然后根据任何实例成员的类型映射一些存根
struct Dog_name { typedef string (Dog::*type); };
template class private_member<Dog_name, &Dog::name>;
...and instance method;
...和实例方法;
struct Dog_bark { typedef string (Dog::*type)(); };
template class private_method<Dog_bark, &Dog::bark>;
do the same with all static instance members
对所有静态实例成员做同样的事情
struct Dog_Species { typedef string *type; };
template class private_member<Dog_Species, &Dog::Species>;
...and static instance methods.
...和静态实例方法。
struct Dog_Legs { typedef int (*type)(); };
template class private_method<Dog_Legs, &Dog::Legs>;
Now you can test them all:
现在您可以全部测试它们:
#include <assert.h>
int main()
{
string name = "Fido";
Dog fido = Dog(name);
string fido_name = fido.*member<Dog_name>::value;
assert (fido_name == name);
string fido_bark = (&fido->*func<Dog_bark>::ptr)();
string bark = "Fido: Woof!";
assert( fido_bark == bark);
string fido_species = *member<Dog_Species>::value;
string species = "Canis familiaris";
assert(fido_species == species);
int fido_legs = (*func<Dog_Legs>::ptr)();
int legs = 4;
assert(fido_legs == legs);
printf("all assertions passed\n");
};
Output:
输出:
$ ./main
all assertions passed
You can look at the sources of test_dog.cppand dog.hpp.
您可以查看test_dog.cpp和dog.hpp的来源。
DISCLAIMER: Thanks to insights of other clever people, I have assembled the aforementioned "library" able to access to private members and methods of a given C++ class without altering its definition or behaviour. In order to make it work it's (obviously) required to know and include the implementation of the class.
免责声明:感谢其他聪明人的见解,我组装了上述“库”,能够访问给定 C++ 类的私有成员和方法,而不会改变其定义或行为。为了使其工作(显然)需要知道并包含该类的实现。
NOTE: I revised the content of this answer in order to follow directives suggested by reviewers.
注意:我修改了这个答案的内容,以遵循审稿人建议的指示。
回答by Mark B
I generally suggest testing the public interface of your classes, notthe private/protected implementations. In this case, if it can't be observed from the outside world by a public method, then the unit test may not need to test it.
我通常建议测试类的公共接口,而不是私有/受保护的实现。在这种情况下,如果不能通过公共方法从外界观察到它,那么单元测试可能不需要对其进行测试。
If the functionality requires a child class, either unit test the real derived class OR create your own test derived class that has an appropriate implementation.
如果功能需要子类,要么对真正的派生类进行单元测试,要么创建自己的具有适当实现的测试派生类。
回答by user1823890
Example from the google testing framework:
// foo.h
#include "gtest/gtest_prod.h"
class Foo {
...
private:
FRIEND_TEST(FooTest, BarReturnsZeroOnNull);
int Bar(void* x);
};
// foo_test.cc
...
TEST(FooTest, BarReturnsZeroOnNull) {
Foo foo;
EXPECT_EQ(0, foo.Bar(NULL));
// Uses Foo's private member Bar().
}
The main idea is the use of the friendcpp keyword. You can extend this example as following:
主要思想是使用friendcpp 关键字。您可以按如下方式扩展此示例:
// foo.h
#ifdef TEST_FOO
#include "gtest/gtest_prod.h"
#endif
class Foo {
...
private:
#ifdef TEST_FOO
FRIEND_TEST(FooTest, BarReturnsZeroOnNull);
#endif
int Bar(void* x);
};
You can define the TEST_FOO preprocessor in two ways:
您可以通过两种方式定义 TEST_FOO 预处理器:
1)within the CMakeLists.txt
1) 在 CMakeLists.txt 中
option(TEST "Run test ?" ON)
if (TEST)
add_definitions(-DTEST_FOO)
endif()
2)as arguments to your compiler
2)作为编译器的参数
g++ -D TEST $your_args