开发软件的时候为代码编写单元测试是很好的. 但实际上, 光有测试还不够, 还要编写好的测试, 这同样重要.
要做到这一点, 考虑遵循一些固执的原则, 对测试代码给予一些关爱:
1. 保持测试代码的紧凑和可读性
要做到这一点, 应该要进行毫不留情的重构, 就像对生产代码应该做的那样. 否则让测试代码随着时间腐化, 就是在测试里面制造可怕的遗留代码. 如果测试不能很容易重构, 那么生产代码也很难重构, 从而导致生产系统的遗留代码. 始终做一个勇敢的重构者.
2. 避免编写重复累赘的断言
举个例子, 测试代码使用正则表达式生成内容, 而这个正则表达式是跟生产代码的解析器中使用的一模一样的.
一般来说, 我们不希望在测试和代码之间复制逻辑. 因此, 在测试中复制正则表达式或其他内容不是一种选择. 在这种情况下, 考虑测试输入激励 / 输出结果之间的关系 (f(输入) -> 输出)可能会有帮助, 例如, 如果代码的目标是要做模板替换, 不要在测试代码里用原始值来做替换. 相反, 在测试里面直接指定预期的计算结果.
- // 使用
- Assertions.assertThat(processTemplate("param1", "param2")).isEqualTo("this is'param1', and this is'param2'"));
- // 而不要用
- Assertions.assertThat(processTemplate("param1", "param2")).isEqualTo("this is'%s', and this is'%s'", param1, param2));
3. 覆盖尽可能多的范围, 包括正面情况, 以及 (甚至更重要的) 出错的代码路径.
通常, 要做到这一点, 最好的办法试采用测试驱动开发(Test Driven Development). 通过 TDD, 人们可以在设计时识别可能会出错的部分. 不要羞于为一段小代码编写一个简单的测试用例. 你永远不知道什么时候, 为什么以及以什么方式, 你会要用到甚至修改这段代码.
可以研究一下如何检查测试的有效性, 类似 PIT 这样的工具可以进行变更测试, 值得研究一下.
4. 不要 Mock 你不拥有的类型!
这不是一个硬界限, 但越过这条线很可能会产生反作用力!
TDD 是关于设计的, 也是关于测试的, 两者一样重要, 在模拟外部 API 时, 测试不能用于驱动设计, API 属于第三方; 这个第三方可以, 并且实际上也经常会更改 API 的签名和行为.
想象一下 Mock 第三方 Lib 的代码. 在第三方库的某次升级之后, 它的逻辑可能会改变, 但测试套件仍会执行得很好, 因为它被 Mock 了. 所以后来, 你认为一切都很好, 毕竟构建墙是绿色的, 软件部署上去, 然后...... 嘣
如果你感觉需要 Mock 第三方库, 可能表明你当前的设计与第三方库没有足够的分离.
另一个问题是第三方库可能很复杂, 需要大量的 Mock 才能正常工作. 这导致过度指定的测试和复杂的测试辅助装置, 这本身就损害了紧凑和可读的目标. 或者由于模拟外部系统过于复杂, 从而导致测试代码对生产代码的覆盖不足.
取而代之的最常见的方法, 是围绕外部 lib / 系统创建包装器, 尽管应该意识到抽象泄漏的风险, 其中过多的低级 API, 概念或异常超出了包装器的边界. 为了验证与第三方库的集成, 编写集成测试, 并使它们尽可能紧凑和可读.
其他人已经写过 Mock 他们不拥有的类型时经历过的痛苦:
5. 不要 Mock 一切, 这是一种反模式
如果一切都被 Mock, 我们真的在测试生产代码吗? 该不 Mock 的时候, 不要犹豫!
不要 Mock 值对象
为什么人们甚至想要这样做?
因为实例化对象太痛苦了! => 不是正当理由.
如果创建新的对象太难了, 那么代码可能需要一些严肃的重构. 另一种方法是为您的值对象创建构建器 - 有一些工具, 包括 IDE 插件, Lombok 和其他. 还可以在测试类路径中创建有意义的工厂方法.
- abstract class CustomerCreations {
- public static Customer customer_with_a_single_item_in_the_basket() {
- // long init sequence
- }
- }
Mockito 专注于对象之间的相互操作, 这是面向对象编程的核心部分.
6. 读《The Growing Object Oriented Software Guided by Tests》这本书
这本书是一本必读书. 这本书带着读者从头开始构建一个完整功能的应用. 作者解释了测试驱动的开发的很多方面, 以及在项目生命周期的不同阶段, 如何做到有效测试.
注: 本文主要内容翻译自 Mockito 的网站文章
来源: https://juejin.im/post/5c3d57f36fb9a049ca379ad1