《单元测试的艺术》设计与可测试性

什么是可测试性设计

如何判断一个系统的可测试性是否良好?可测试性良好的系统应该能够低成本且快速地编写具有一下特征的测试用例:

  1. 运行速度快
  2. 相互隔离
  3. 不需要外部配置
  4. 稳定

 可测试性设计:修改代码设计使其更容易被测试。

关于可测试性设计的争论

是否需要做可测试性设计?可测试性设计必然会增加代码量和工作量,并且过多的抽象可能会使得代码变得更加复杂。

作者的观点:如果代码具有可扩展性和抽象化,则很容易在其中找到测试可以利用的“接缝”。实际上,可测试性设计与SOLID原则相关,可测试性好的设计并不一定就是好设计。

我们也许应该不再将可测试性作为设计的最终目标,而是只追求好的设计,可测试性即是附随的结果。

 

可测试性的本质

  1. 可测试性并不是需要刻意追求的,例如动态语言天生就是可测试的,因为可以在运行时动态地替换任何东西,所有的东西都能替换。作者认为,这样的情况不需要做可测试性设计。

  2. 使用工具会改变我们思考的方式:例如如果系统能够帮助程序员管理内存,那么就不需要左内存管理相关的设计。如果有一个工具,可以解决可测试性问题,那么我们就不必为可测试性进行特殊设计。
  3. 不可测试的设计的主要问题在于无法在运行时替换依赖项,因此我们需要创建接口,把方法设置为虚拟...如果有不受限的框架,那么我们就不再需要把可测试性作为一项设计目标了。

 

可测试性设计的方法

下图列举了书中提到的七个方法,具体看书中代码片段更容易理解:

 

 

 

 

补充理解

1. 本章阶主要关注的是用例编写层面的成本,有了stub和mock等工具,在设计阶段我们确实可以减少对于接口的可测试性设计的关注。

2. 影响测试用例运行结果的因素包括:1)用例输入;2)全局变量(初始化流程);3)依赖(依赖其他模块或桩函数)。

针对这三点,测试代码的质量(主要是稳定性)是依赖于被测代码的可测试性质量的,因此说可测试性是设计出来的。其中的关键是能否找到稳定的边界:

  1)局部更好的模块化设计;

  2)测试面向稳定的接口边界、定义抽象接口,依赖注入

3. 测试边界:在分层的系统架构中,层与层之间有严格的单向依赖限制,但是模块内部的依赖是比较混乱的。如果模块是基于全局变量的过程化设计,全局变量不仅作为模块的输入参数(否则函数可能不能正常工作),也作为模块的输入参数(也需要断言一堆的全局变量),这样的代码的测试性成本特别大,模块内部耦合太杂乱,找不到稳定清晰的软件测试边界。当以模块作为测试入口时,模块对周边模块有依赖,需要打一堆的桩函数,因此测试的成本也比较大;如果是以层一级为边界时,就比较稳定。可以说,团队的最小的测试边界,基本上就等同于能设计最小的松耦合的测试的边界。如果能设计的最小测试边界比较大(比如层级别),但是要去测试不稳定的接口时(比如模块级别),测试成本和可维护性都会很大。这样的用例可能就会很脆弱,一开始为了覆盖率上上去,一旦稍微一重构或者需求变更,可能用例就作废了。

posted @ 2022-10-19 21:37  Han-helloWorld  阅读(93)  评论(0编辑  收藏  举报