单元测试原则清单

关于单元测试,网上多为描述具体实战和其重要性,很少针对单元测试方式和原则做进一步说明,而实际工作过程中很多开发者不知道应该测试,Jean-baptiste Rieu写了《Unit Testing Checklist: Keep Your Tests Useful and Avoid Big Mistakes》一文,他对单元测试中的原则和思想整理为一个checklist,相信对开发者有很大帮助。

1. 为何使用单元测试

它可以测试现有以及未来的功能模块,保证了代码质量。它强制你书写具有可测性,低耦合的代码。这比手工回归测试廉价的多。它将提高代码可行度,协助团队工作。

2. 测试步骤

单元测试是验证你代码的一些常用方法集合。按照下面的步骤操作是个不错的方法:

  • 写被测类的API;
  • 写一个方法测试API;
  • 实现这个API;
  • 执行单元测试;

3. 测试原则(检查清单)

为何需要个检查清单?单元测试比实际实现可能还要难一些,它强迫你考虑清楚一些事情。但单元测试本身应该简单、直接、易用和易于维护。你还要知道何时停止写测试并且开始写实现。使用这个检查清单能够确保有效测试且达到目标,该清单能帮你避免一些明显的错误。

  • 一个测试类只能对应一个被测类。你要测试的是对应类API的正确性,结果是所期望的。
  • 每次只测试一个方法。确保不要测试私有方法,它们是被封装起来的,并不是API。
  • 测试用例的变量和方法都要有明确的含义。比如,将预期结果保存到 expectedFoo 变量而不是只保存到 foo。如果要测试很多复合结果,可使用组合变量名称诸如:inputValue_NotNull, inputValue_ZeroData, inputValue_PastDate等等 (这要看你的代码规范约定)。
  • 测试用例易读。以后的维护者会在阅读实现前去阅读你的测试,这将帮助他们在调试前理解被测类的逻辑。
  • 测试用例干净整洁。
    • 程序中不要有流程控制语句(switchif 等)。一个好的测试用例处理顺序简单直接,准备数据,验证结果顺序,如有必要,使用子方法分解结构,让测试用例更加易读。如果是多场景,使用多个方法测试;
    • 例如,一个测试用例的方法代码长度最多满屏,不要有滚动条,大概在 1 到 20 行左右。如果测试代码太长,考虑拆开成多个方法,以避免混在一起相互干扰;
  • 测试用例要验证预期的异常。Java中使用 @Test(expected=MyException.class)。
  • 测试用例不要连接数据库。或者说,如果测试中需要连接数据库操作,必须使用mock,每个新的测试方法都自主引导到临时数据库(使用 Setup/Teardown做准备)。
  • 测试用例不要连接网络资源。测试某个方法时无法确保第三方诸如网络和设备的有效性 (使用mocks)。
  • 测试用例要处理好边界情况,极限值 (max, min) 和null变量(即使抛异常)。你要确保这些问题状况永远都不会发生,甚至在维护时不使用测试用例。
  • 测试用例在任何情况下都可运行,并且不需要配置和人工干预。
  • 测试用例通过当前测试,并且易于改进。测试用例要能够支持代码的演变,如果很难维护或者代码太轻而不能细化,那就成了负担(很多人不写单元测试用例就是这个原因)。
  • 测试用例要具体。在Java中,测试的方法不要使用 Date()作为输入,最好使用Calendar创建具体格式的时间(别忘了设置时区)。再如:name = “Smith”; 不要用 name = “name”或者 name = “test”;
  • 测试用例使用mock来模拟复杂的类结构或方法。
    • 记住一次只测试一个类;
    • 不要在自己的类中测试第三方的类库. 类库应该由它们自己的测试用例来测试(这也是选择选择类库的一个好方式);
  • 测试用例不要使用@ignored或者被注释掉,切记切记。
  • 测试用例帮我验证了代码架构。如果你不能测试某个方法或者类,那么你的设计就不够灵活。
  • 测试用例可以运行在任何平台,而不仅是指定目标平台。不要指望一个特定设备或者硬件配置。不然你的测试用例会迁移困难,这样会导致你会禁用他们。
  • 测试用例运行速度快。
    • 慢的测试会把你拖垮,快的速度会鼓励你经常运行他们,它还能帮助你减少持续集成系统上的构建时间;
    • 使用测试运行程序时允许你一次启动一个测试。慎用“delay” 或者 “sleep” ,比如只有在某些边缘情况下,比如等待通知或者基于时钟的方法;

发表评论