【问题标题】:How to access private members in unit tests?如何在单元测试中访问私有成员?
【发布时间】:2016-02-28 12:22:26
【问题描述】:

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

第一个处理这个问题的方法是,编写一个预处理器定义写入 publis 而不是 private 和 protected。

#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;
};

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

谢谢。

【问题讨论】:

  • 我只想添加friend class Unit_test,比如。 不要使用#define。如果包含任何标准库头文件,则为未定义行为。
  • 你为什么需要那个?如果无法通过类型的公共或受保护接口检测到错误,则没关系。如果您在类的私有部分有复杂的功能,您可以考虑将其分解为独立的、可测试的组件。
  • 单元测试验证类合同的合规性(或 API,如果您愿意),而不是内部实现。您的单元测试类永远不必切换私有成员/方法,这超出了他们的范围。
  • 不测试实现;测试合同。如果您分解您的代码,请通过他们的合约递归地对分解后的单元进行单元测试。
  • “在开发单元测试时,可能需要访问私有成员来检查类的内部状态。” - 不,不能。像这样使用“单元测试”意味着白白破坏你的软件架构。

标签: c++ unit-testing


【解决方案1】:

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

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

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

【讨论】:

  • 感谢您的建议。我将更多地关注 TDD。我目前缺少一些东西。
【解决方案2】:

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

它破坏了封装。

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

谁来编写单元测试?

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

如果不是编写类的人,他们如何知道私有成员应该和不应该包含哪些内容?该人将不得不检查内部算法,以发现在什么时候私有成员中需要有什么值。他们也很可能受到代码所说而不是规范要求的影响。他们也可能会将代码中的错误引入测试中,从而使他们免于通过。

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

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

测试应该从规范的角度来设计,它们应该是关于违反合同的。它们不应该与您发现算法中的错误的能力有关。如果测试失败,那是程序员的工作。测试人员的工作是根据外部刺激关注因果关系。他们应该问的问题是“班级是否按照广告宣传的那样做?”不是“代码是否正确执行?”。

这并不是说程序员不应该在编写代码时考虑到测试。可以设计类以使其合同更可测试。但是编写测试的人不应该知道或关心, 类是如何完成它的工作的,只需要它根据规范正确地完成它。

说到测试,规范才是王道,而不是实施

所以我认为当您将实现规范进行比较时,测试是有效的。如果您开始研究实现,那么您开始将一个人的实现应该如何工作的想法与另一个人的实现应该如何工作的想法相提并论,他们冒着将自己的实现错误引入其中的风险测试或加强原始程序员的错误。

【讨论】:

    【解决方案3】:

    这类事情没有“最佳实践”。只是主观意见不同。

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

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

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

    最好尽可能多地设计你的类,这样就没有必要检查类的内部状态了。但是,我意识到在某些 V&V 制度中,有必要提供单独的证据来证明类的成员函数已正确实现 - 如果不检查类私有数据,这可能很难实现。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-07-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-12-20
      • 2011-09-28
      • 1970-01-01
      相关资源
      最近更新 更多