当前位置:网站首页>本周小贴士#135:测试约定而不是实现
本周小贴士#135:测试约定而不是实现
2022-07-07 15:39:00 【-飞鹤-】
作为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 从测试中访问私有成员。
- 不要与整个测试装成为友元。 使用上述更有针对性的方法之一。
边栏推荐
- Reflections on "product managers must read: five classic innovative thinking models"
- Rpcms method of obtaining articles under the specified classification
- Several best practices for managing VDI
- The server is completely broken and cannot be repaired. How to use backup to restore it into a virtual machine without damage?
- Share the latest high-frequency Android interview questions, and take you to explore the Android event distribution mechanism
- How to add aplayer music player in blog
- [Huang ah code] Why do I suggest you choose go instead of PHP?
- MySQL implements the query of merging two fields into one field
- DevOps 的运营和商业利益指南
- 【视频/音频数据处理】上海道宁为您带来Elecard下载、试用、教程
猜你喜欢
【TPM2.0原理及应用指南】 12、13、14章
【网络攻防原理与技术】第6章:特洛伊木马
DevOps 的运营和商业利益指南
Is AI more fair than people in the distribution of wealth? Research on multiplayer game from deepmind
鲲鹏开发者峰会2022 | 麒麟信安携手鲲鹏共筑计算产业新生态
Sator推出Web3遊戲“Satorspace” ,並上線Huobi
Shallow understanding Net core routing
策略模式 - Unity
MRS离线数据分析:通过Flink作业处理OBS数据
The process of creating custom controls in QT to encapsulating them into toolbars (II): encapsulating custom controls into toolbars
随机推荐
【TPM2.0原理及应用指南】 5、7、8章
The computer cannot add a domain, and the Ping domain name is displayed as the public IP. What is the problem? How to solve it?
AI来搞财富分配比人更公平?来自DeepMind的多人博弈游戏研究
Linux 安装mysql8.X超详细图文教程
鲲鹏开发者峰会2022 | 麒麟信安携手鲲鹏共筑计算产业新生态
mysql使用笔记一
企业即时通讯软件是什么?它有哪些优势呢?
[source code interpretation] | source code interpretation of livelistenerbus
专精特新软件开发类企业实力指数发布,麒麟信安荣誉登榜
LeetCode 312. Poke balloon daily
LeetCode 403. Frog crossing the river daily
DNS series (I): why does the updated DNS record not take effect?
LeetCode 1049. Weight of the last stone II daily question
【网络攻防原理与技术】第6章:特洛伊木马
Leetcode brush questions day49
策略模式 - Unity
LeetCode 648(C#)
Flask搭建api服务-SQL配置文件
无法链接远程redis服务器(解决办法百分百)
LeetCode 1155. N ways to roll dice one question per day