如何在单元测试中访问私有成员

How to access private members in unit tests?

本文关键字:成员 访问 单元测试      更新时间:2023-10-16

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

处理这个问题的第一种方法是编写预处理器定义写入 publis 而不是私有和受保护。

#define protected public
#define private public

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

class test_foo {
};
class foo {
private:
  int mem;
  friend class test_foo;
};

第三种方法是创建用于测试的公共接口。

class foo {
#if defined FOO_UNIT_TEST
public:
  int get_mem() const;
#endif
private:
  int mem;
};

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

谢谢。

在我看来

,这三种解决方案都是丑陋的。我想说的是,最佳做法是不在单元测试中检查类的内部状态,并且不要仅公开单元测试的方法。这样做违背了TDD的原则。

一个好的单元测试不对内部状态做出任何假设,如果类实现完全更改,它将不加修改地运行。

为什么需要检查内部状态?也许您有设计问题,可以通过重新设计而不是黑客来更好地解决。您是否有更全面的例子来说明您要实现的目标?

这就是为什么我觉得在这个细节上测试类从根本上说是一个坏主意。

它打破了封装。

测试之所以有效,是因为它是从规范的角度而不是编程的角度对单元功能进行冷静的评估。当从程序员的角度编写测试时,你可能会破坏这个目的。

谁来编写单元测试?

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

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

最重要的是,我觉得从实现者的角度测试类本质上是有缺陷的,并且会邀请测试本身和他们正在测试的代码一样有缺陷。

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

测试应该从规范的角度设计,并且应该打破合同。它们不应该是关于你发现算法中错误的能力。如果测试失败,这就是程序员的工作。测试人员的工作是根据外部刺激专注于因果关系。他们应该问的问题是"类是否做了它所宣传的事情?"而不是"代码是否正确?"。

这并不是说程序员不应该在编码时考虑测试。可以设计类以使其协定更易于测试。但是编写测试的人不应该知道或关心类是如何完成它所做的事情的,而应该知道它是否按照规范正确地完成了它。

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

因此,我认为当您将实现规范进行对比时,测试是有效的。如果你开始查看实现,那么你就会开始将一个人关于实现应该如何工作的想法与另一个人关于实现应该如何工作的想法进行对比,他们冒着将自己的实现错误引入测试或强化原始程序员错误的风险。

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

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

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

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

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