本周小贴士#135:测试约定而不是实现

Posted -飞鹤-

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了本周小贴士#135:测试约定而不是实现相关的知识,希望对你有一定的参考价值。

作为TotW#135最初发表于2017年6月5日

由James Dennett创作

“如果你有一个真正的朋友,那么你拥有的不仅仅是你所拥有的”——托马斯·富勒

C++有一个使用公有的,保护的,私有的和友元的详细访问控制机制。测试代码有它自己使用这些装置的规则,GoogleTest使用它的FRIEND_TEST宏来扩充它们。使用FRIEND_TEST应该是最后的手段,而不是优先选项。

测试约定

我们编写测试来发现组件约定实现中的错误,或者让我们有足够的信心相信这里没有此类错误。在使用测试驱动开发(TDD)时,我们还编写测试来帮助我们设计约定。依赖于组件未指定内容的测试很脆弱,即便生产代码正常工作,也容易报告失败。

优先通过组件的公共接口进行测试。更通用地说,测试应该验证组件的约定,并且与任何其他客户端一样,不应该做出超过保证的假设。

提供来自测试访问的技术

许多技术能够被用来允许测试代码完成其工作的必要访问。这里列举出一些,大致从好到坏进行排列。

为测试增加公共API

在通过最小接口进行测试时,有时很难获得足够的覆盖率。如果您的组件正在实现由基类指定的非常狭窄的接口(例如,只有一个 ProcessItem 虚函数的接口)并且从仅使用该接口的测试中获得足够的信心是不切实际的,请考虑创建一个新的、可测试的组件,其中包含实施细节。那么包含虚函数的类可以非常简单,只需要最少的测试。 BUILD 可见性可用于限制您的实现类的使用(如果需要并且您的构建系统支持它)。

如果测试仅依赖于一两个私有函数,请考虑将这些函数作为公共接口的一部分。这还不错:无论如何,您都需要它们具有清晰记录的接口,并且其他客户端(不仅仅是测试)可能会发现它们很有用。如果在考虑之后,您决定一个函数确实只用于测试,那么它应该被记录下来,并且可能以 ForTesting 后缀命名。

使用对等体来避免暴露实现

如果测试仍然需要访问私有实现细节,请创建一个测试对等体(有时称为测试配对)。 测试对等体是被测类的友元类,通常在 _test.cc 文件中定义(尽管有些人更喜欢将其定义在与友元类相同的文件中),并用于提供对待测试类的受控访问的测试代码。 测试对等体将不能存在于匿名命名空间中,因为它的确切名称需要与朋友声明相匹配,但测试代码的其余部分可以像往常一样位于匿名命名空间中。 测试对等类的名称通常以 Peer 结尾。

最后的手段:使用FRIEND_TEST

虽然在老代码中很常见,但是FRIEND_TEST不应该被用在新代码中。它引入了反向耦合,使生产代码头文件依赖于相关单元测试的细节。它强制测试从匿名命名空间中移出。每个FRIEND_TEST都授予一个测试函数不受约束地访问待测类;在长的测试函数中,很难发现测试在哪里修改了待测类的状态。它需要使用由GoogleTest提供的一个不常见的头文件去包含在生产代码中,然而几乎所有的GoogleTest仅用于测试。最后它的扩展性很差,在添加新测试时需要将新的FRIEND_TEST添加到生产的头文件中。在实践中,这通常会导致头文件拥有冗长的FRINED_TEST块。

不要让整个测试装置成为友元

强烈建议不要让整个测试装置成为待测类(带有友元类MyClassTest)的友元。与上述选项比较,它允许整个测试装置(但不是测试本身,它们是测试装置的子类)不受限并且无注释地访问待测类的每个成员,这意味着测试代码的读者没有测试何时打破封装的可见的线索。它还强迫测试装置位于匿名命名空间之外。与友元装置相比,测试对等体使代码更加自我注释化,并且代码作者只需要花费一点额外的工作。

建议摘要

  • 优先测试组件的客户端接口,并且保证测试独立于私有实现的细节。
  • 如果客户端接口不满足彻底运行待测单元,则将可测试的,可能仅用于测试的子组件分解出来。
  • 有时添加到公共接口以使组件可测试是合理的。
  • 如有必要,请使用测试对等体而不是使用 FRIEND_TEST 从测试中访问私有成员。
  • 不要与整个测试装成为友元。 使用上述更有针对性的方法之一。

以上是关于本周小贴士#135:测试约定而不是实现的主要内容,如果未能解决你的问题,请参考以下文章

本周小贴士#55:名字计数与unique_ptr

本周小贴士#140:常量:安全习语

本周小贴士#140:常量:安全习语

本周小贴士#103:Flag是全局变量

本周小贴士#146:默认vs值初始化

本周小贴士#146:默认vs值初始化