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

C++ 如何对C+的私有成员(和方法)进行单元测试+;班级,c++,unit-testing,private-members,private-methods,C++,Unit Testing,Private Members,Private Methods,我对单元测试非常陌生,有点困惑 我试图在一个C++类上进行单元测试(使用升压单元测试框架),称为变量IMPL/。详情如下 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 Rec

我对单元测试非常陌生,有点困惑

<>我试图在一个C++类上进行单元测试(使用升压单元测试框架),称为<代码>变量IMPL/<代码>。详情如下

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
  }
};
类变量
{
公众:
void UpdateStatistics(void){
//基于m_-val计算均值并更新m_-mean;
OtherClass::SendData(m_平均值);
m_val.clear();
}
虚空记录数据(双)=0;
受保护的:
std::向量m_val;
私人:
双m_均值;
};
类VariableImpl:公共变量
{
公众:
虚拟无效记录数据(双d){
//将数据放入m_val
}
};
我的问题是如何检查平均值计算是否正确?请注意,1)
m_mean
受保护,2)
UpdateStatistics
调用另一个类的方法,然后清除向量

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

我该怎么办

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

蒂亚


Jir

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

对于私人课程,请介绍一个friend类。这不是最好的解决方案,但可以为您解决问题

还是这个黑客

#define private public

单元测试VariableImpl,如果它的行为得到了保证,那么变量也是如此

测试内部构件并不是世界上最糟糕的事情,但目标是只要接口契约得到保证,它们可以是任何东西。若这意味着创建一堆奇怪的模拟实现来测试变量,那个么这是合理的


如果这看起来太多了,请考虑实现继承不会造成关注的严重分离。如果很难进行单元测试,那么这对我来说是一种非常明显的代码味道。

我通常建议测试类的公共接口,而不是私有/受保护的实现。在这种情况下,如果不能通过公共方法从外部观察到它,那么单元测试可能不需要测试它

如果功能需要一个子类,可以对真正的派生类进行单元测试,也可以创建您自己的测试派生类,该类具有适当的实现。

好的,单元测试应该测试单元,理想情况下,每个类都是一个自包含的单元–这直接遵循单一责任原则

因此,测试类的私有成员不应该是必要的——类是一个黑匣子,可以按原样在单元测试中覆盖

另一方面,这并不总是正确的,有时有很好的理由(例如,类的几个方法可能依赖于应该测试的私有实用程序函数)。一个非常简单、非常粗糙但最终成功的解决方案是在包含定义类的头文件之前,将以下内容放入单元测试文件中:

#define private public

当然,这破坏了封装,是邪恶的。但对于测试来说,这是有目的的。

在c++中测试受保护数据的好方法是分配一个friend代理类:

#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(); 
}
请参阅更多goolge测试(gtest):

一般来说,我同意其他人在这里所说的——只有公共接口应该进行单元测试。然而,我刚刚遇到了一个案例,我必须首先调用一个受保护的方法,以准备一个特定的测试用例。我首先尝试了上面提到的
#定义受保护的公共
方法;这在Linux/gcc中有效,但在Windows/VisualStudio中失败。原因是将
protected
更改为
public
也会更改损坏的符号名称,从而给我带来链接器错误:库提供了受保护的
\uuuuudeclspec(dllexport)void Foo::bar()
方法,但在
\define
就位后,我的测试程序预期会出现public
\uuudeclspec(dllimport)void Foo::bar()
方法,该方法给了我一个未解决的符号错误

出于这个原因,我切换到基于
朋友的解决方案,在我的类标题中执行以下操作:

// 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;
};
在我的实际测试案例中,我做到了:

#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();
#包括
#包括
名称空间单元测试{
//私有/受保护方法的静态包装器
结构脚酯
{
静态bool somePrivateMethod(Foo&Foo,int-bar)
{
返回foo.someprivatethod(bar);
}
};
}
增压自动测试套件(最足部);
BOOST\u AUTO\u TEST\u案例(testsomeprivatethod)
{
//只是一个愚蠢的例子
富富,;
BOOST_CHECK_EQUAL(单元测试::FooTester::someprivatethod(foo,42),true);
}
BOOST_AUTO_TEST_SUITE_END();
这适用于Linux/gcc以及Windows/VisualStudio。

主要思想是使用朋友cpp关键字。 您可以如下扩展此示例:

// 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);
};
您可以通过两种方式定义TEST_FOO预处理器:

1) 在CMakeLists.txt中

option(TEST "Run test ?" ON)
if (TEST)
  add_definitions(-DTEST_FOO)
endif()
2) 作为编译器的参数

g++ -D TEST $your_args

虽然我认为测试一个类的私有成员/方法的需要是代码嗅觉,但我认为C++中技术上是可行的。 例如,假设您有一个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";
然后根据任何实例成员的类型映射一些存根

struct Dog_name { typedef string (Dog::*type); };
template class private_member<Dog_name, &Dog::name>;
您可以查看和的来源

免责声明:由于其他的见解,我已经组装了上述的“库”,它能够访问给定的C++类的私有成员和方法,而不改变其定义或行为。为了让它正常工作,(显然)需要知道并包含类的实现


注意:我修改了这个答案的内容,以遵循评论员建议的指示。

在我看来,有一个
朋友
上课太麻烦了。@Konrad Rudolph-是的,这就是为什么我说这不是最好的解决方案。我会假设t
#include "privablic.h"
#include "dog.hpp"
struct Dog_name { typedef string (Dog::*type); };
template class private_member<Dog_name, &Dog::name>;
struct Dog_bark { typedef string (Dog::*type)(); };
template class private_method<Dog_bark, &Dog::bark>;
struct Dog_Species { typedef string *type; };
template class private_member<Dog_Species, &Dog::Species>;
struct Dog_Legs { typedef int (*type)(); };
template class private_method<Dog_Legs, &Dog::Legs>;
#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");
};
$ ./main
all assertions passed