如何对C++类的私有成员(和方法)进行单元测试

Jir*_*Jir 28 c++ unit-testing private-members private-methods

我对单元测试很新,我有点困惑.

我试图在一个名为C++的类上进行单元测试(使用Boost单元测试框架)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
  }
};
Run Code Online (Sandbox Code Playgroud)

我的问题是如何检查均值是否正确计算?注意1)m_mean受保护,2)UpdateStatistics调用另一个类的方法,然后清除向量.

我能看到的唯一方法是添加一个getter(例如GetMean),但我根本不喜欢这个解决方案,也不认为它是最优雅的.

我应该怎么做?

如果我要测试私有方法而不是私有变量,我该怎么办?

TIA,

JIR

Kon*_*lph 49

那么,单元测试应该测试单元,理想情况下每个类都是一个独立的单元 - 这直接来自单一责任原则.

因此,测试一个类的私有成员不是必需的 - 该类是一个黑盒子,可以在单元测试中按原样覆盖.

另一方面,这并不总是正确的,有时有充分的理由(例如,类的几个方法可能依赖于应该测试的私有效用函数).一个非常简单,非常苛刻但最终成功的解决方案是包含定义类的标题之前将以下内容放入单元测试文件中:

#define private public
Run Code Online (Sandbox Code Playgroud)

当然,这会破坏封装并且是邪恶的.但是为了测试,它有助于实现目的.

  • 任何公共/私人关键词之前的方法都不会公开,顺便说一句 (5认同)
  • 虽然这个答案适用于大多数编译器,但它不符合标准.您不能使用与关键字相同的#define语句.参见C++ 98:第17.4.3.1.1节 (4认同)
  • 看来这个“定义”是最不引人注目的解决方案。我想我面临的问题更多是由于我的课程没有很好地遵循单一责任原则。关于我提出的玩具示例,您是否建议我创建一个“Mean”类来用作“Variable”的私有成员?这样“Mean”就可以毫无问题地进行测试。然而,缺点是类别的激增。 (2认同)

Dum*_*der 11

对于受保护的方法/变量,从类继承Test类并进行测试.

对于私人,介绍一个朋友类.它不是最好的解决方案,但可以为您完成工作.

或者这个黑客

#define private public
Run Code Online (Sandbox Code Playgroud)

  • 在我看来,拥有一个"朋友"课程太过于干扰. (5认同)
  • 定义关键字是**未定义的行为**:http://stackoverflow.com/questions/27778908/define-private-to-public-in-c (3认同)

min*_*iot 6

总的来说,我同意其他人在这里所说的话 - 只有公共接口应该进行单元测试.不过,我刚才有一个案例,我必须首先调用受保护的方法,为特定的测试用例做准备.我首先尝试了#define protected public上面提到的方法; 这适用于Linux/gcc,但在Windows/VisualStudio中失败了.原因是更改protectedpublic也改变了受损的符号名称,因此给了我链接器错误:库提供了一个受保护的 __declspec(dllexport) void Foo::bar()方法,但是#define在适当的位置,我的测试程序期望一个公共 __declspec(dllimport) void Foo::bar()方法,它给了我一个未解决的符号错误.

出于这个原因,我切换到一个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;
};
Run Code Online (Sandbox Code Playgroud)

在我的实际测试案例中,我这样做了:

#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();
Run Code Online (Sandbox Code Playgroud)

这适用于Linux/gcc以及Windows/VisualStudio.