冗余测试会提高维护成本。
避免条件逻辑
条件逻辑会让你的单元测试更难以维护,出问题不容易排查,不够精准;
单测需要确定性
避免脆弱测试,Mock不确定的依赖:时间、随机数、并发性、基础设施、现存数据、持久化、网络等等。
测试快速执行
避免sleep等操作,导致测试执行缓慢。
避免过度指定
对于过度指定的讨论,其核心问题就是要我们判断哪些是单元测试应该覆盖的,哪些是应该留给其他测试手段的。如果一个场景,单元测试覆盖之后,导致经常单测失败,需要不断更新维护,那就可以考虑不做单元测试覆盖。
像素完美是一个典型的、经常拿出来讨论的例子,Flutter的Golden Test就是一个golden master testing的例子;《有效的单元测试》中关于像素完美的讨论:
像素完美:顾名思义,是一种特定于图形和图像生成的测试坏味道。它混杂了魔法数字和基本断言,使得测试极难阅读也极其脆弱。
这种测试几乎无法阅读,因为即使测试在语义上是处于高层概念的,却仍然会针对硬编码的底层细节例如像素坐标和颜色来进行断言。指定坐标上的像素是黑还是白,与两个图形是否相连或堆叠的概念是有区别的。
这种测试极其脆弱,因为即使很小的和不相关的输入变化——是否是另一个图像,或图形对象的渲染方式——都足以影响输出、打破测试,谁让你非要精确地检查像素坐标和颜色呢。同样的问题在采用golden master技术时也会遇到,其做法是事先将图像录制下来,并手工检查其正确性,以后再进行测试时就将渲染出的图像与之进行比对。
这些可不是我们愿意去维护的测试。我们不希望带着这种脆弱的精确度去编写测试,而是使用模糊匹配和智能算法来代替繁琐的数值比较。
对于特定场景,Golden Test是一个非常有效的手段,但需要非常谨慎的评估;慎用Golden Test!
不要写永不失败的测试,不要写没有校验的测试
单测需要对明确的逻辑校验,永不失败的测试或者没有校验的测试是不可信赖的。
测试不要名不副实
避免测试的描述与测试内容不符;测试结果必须精准;测试该失败的时候一定要失败!
测试私有或者受保护的方法
解决思路:
- 将方法变成公共方法;
- 将方法抽取到新类;
- 将方法变成静态方法;
- 将方法成为测试可见方法。
避免强制的测试顺序
依赖测试顺序导致测试可靠性变得脆弱,未来维护成本变高。
清理测试环境
在teardown阶段清理测试环境,例如还原全局的Config、清理创建的文件目录等等。
统一的单测命名、变量命名
统一的单测命名可以提高可读性、可维护性。
使用有意义的断言
断言的错误信息要有意义,出现问题能够明确错误的原因。
把单元测试视为“一等公民”
测试用例应该被视为“一等公民”:同样需要代码评审,同样需要代码质量检查,确保单元测试的有效性。
单元测试代码评审的过程,也是团队同学互相学习的过程,沉淀最佳实践的过程。
加速执行速度
日常对单测执行时间进行监控,对测试进行性能分析,优化执行时间过长的测试用例。
测试金字塔
测试金字塔是Mike Cohn在他的著作《Succeeding with Agile》一书中提出了这个概念。测试金字塔是一个比喻,它告诉我们要把软件测试按照不同粒度来分组。它也告诉我们每个组应该有多少测试。
为了维持金字塔形状,一个健康、快速、可维护的测试组合应该是这样的:写许多小而快的单元测试。适当写一些更粗粒度的测试,写很少高层次的端到端测试。注意不要让你的测试变成冰淇淋或者沙漏那样子,这对维护来说将是一个噩梦,并且跑一遍也需要太多时间。
避免测试重复
在实现测试金字塔时,你也应该牢记这两条基本法则:
1、如果一个更高层级的测试发现了一个错误,并且底层测试全都通过了,那么你应该写一个低层级测试去覆盖这个错误;
2、竭尽所能把测试往金字塔下层赶。
如果你已经在低层级测试里覆盖了所有情况,那么再维护一个高层级的测试就没有必要了。警惕沉没成本的思维陷阱,果断摁下删除键。没有理由在不再提供价值的测试上浪费宝贵时间。
补充单元测试应该从哪里开始
单元测试应该及时编写,就算没有实践TDD,也应该在代码实现之后尽快编写单元测试,避免写出不可测试的代码,也可以让bug尽早暴露。
但很不幸的,我们很多时候在刚开始卓越工程,推广单元测试的时候,不得不面对补充单元测试的情况;这绝对是一个有挑战的事情。