关于c ++:如何在单元测试中访问私有成员?

How to access private members in unit tests?

在开发单元测试时,访问私有成员以检查类的内部状态可能是必要的。 有时getter和setter函数不可用,有时它们不公开。

处理它的第一种方法是,编写预处理器定义写入publis而不是private和protected。

1
2
#define protected public
#define private public

第二种方法是让测试类成为类的朋友。

1
2
3
4
5
6
7
8
9
class test_foo {
};

class foo {
private:
  int mem;

  friend class test_foo;
};

第三种方法是为测试创建公共接口。

1
2
3
4
5
6
7
8
9
10
class foo {

#if defined FOO_UNIT_TEST
public:
  int get_mem() const;
#endif

private:
  int mem;
};

除了这些方法之外还有其他方法吗? 每种方法都有利有弊。 哪一个可以被认为是最佳实践?

谢谢。


在我看来,这三种解决方案都很难看。我想说最好的做法是不在单元测试中检查你的类的内部状态,而不是仅仅为单元测试公开方法。这样做违背了TDD的原则。

良好的单元测试不会对内部状态做出任何假设,如果类实现完全改变,则无需修改即可运行。

为什么检查内部状态是必要的?也许你的设计问题可以通过重新设计而不是黑客来解决。你有一个更全面的例子来说明你想要实现的目标吗?


这就是为什么我认为在这个细节中测试类基本上是一个坏主意。

它破坏了封装。

测试是有效的,因为它是从规范的角度对单元功能的冷静评估,而不是编程。从程序员的角度编写测试时,你可以打败这个目的。

谁会写单元测试?

如果是编写课程的人,那么他已经对算法如何工作以及应该在哪里有自己的想法。他可能会编写测试来适应他引入的大多数错误,这些错误使得测试与代码一样错误,更糟糕的是,给错误的代码一个免费的通行证。

如果是没有写过课程的人,他们怎么知道私人成员应该和不应该包含什么?那个人必须检查内部算法,以便发现私人成员在什么点需要什么值。它们也可能受到代码所说的内容的影响,而不是规范要求。他们也可能会将代码中的错误引入测试,为他们提供免费通行证。

最重要的是,我认为从实施者的角度来测试类本质上是有缺陷的,并且邀请测试本身就像他们测试的代码一样错误。

让第二副眼睛看/共同开发代码是一件好事,但那是编码阶段,而不是测试阶段。

测试应该从规范的角度进行设计,它们应该是打破合同。它们不应该是您在算法中发现错误的能力。如果测试失败,那就是程序员的工作。测试者的工作是专注于基于外部刺激的因果关系。他们应该问的问题是"班级是否做了宣传的做法?"不是"代码是否正常运行?"。

这并不是说程序员不应该考虑测试代码。可以设计类以使其合同更易于测试。但是,编写测试的人不应该知道或关心类如何完成它所做的事情,只能根据规范正确地完成它。

在测试方面,规范是王道,而不是实施。

所以我认为当您根据规范进行实施时,测试是有效的。如果你开始研究实现,那么你就开始考虑一个人的想法,即如何实现应该如何解决另一个实现如何工作的想法,并且他们冒着将自己的实现错误引入测试或者加强原始版本的风险程序员bug。


在这种事情上没有"最佳实践"。只有不同的主观意见。

您的第一个选项不是一个好习惯,因为使用预处理器重新定义语言关键字被指定为给出未定义的行为。可能引入未定义行为的测试用例可能会因为与被测试类的特征完全无关的原因而失败或(甚至更糟)通过。

在这三个中,我可能会鼓励第二种方法(进行单元测试的朋友类)。这样,测试可以以不依赖于被测试类的任何特定功能的方式实现,但可以检查它喜欢的任何内容。

第三种选择具有与类的其他成员函数冲突或从基类继承的潜在缺点。我记得有一个同事遇到类似技术的问题,当测试函数碰巧与基类中声明的虚函数具有相同的签名时(其中一个函数使用具有多个不同含义的英文单词命名)因此,测试功能导致其他明显不相关的测试失败。

最好尽可能地设计你的类,因此不必检查类的内部状态。但是,我意识到有些V&V制度需要提供单独的成员函数正确实现的单独证据 - 如果不检查类私有数据就很难实现。