为何使用单元测试
它可以测试现有以及未来的功能模块,保证了代码质量。它强制你书写具有可测性,低耦合的代码。这比手工回归测试廉价的多。如果测试不匹配,可以考虑修改单元测试或者修改代码结构,这可以帮助判断合理性,而不是直接让不合理的代码上线。它将提高代码可行度,协助团队工作
测试原则
- 不应该编写成功通过的单元测试:它们应该被写成不通过的。你可以在几分钟内让任何一组测试通过,但这只是在欺骗你自己
- 测试类应该只测试一个功能:你应该用一个功能去测试一个方法。否则,你会违反了单一职责原则
- 测试类具备可读性:确保测试类标有注释并且容易理解,就像其他的代码一样。以后的维护者会在阅读实现前去阅读你的测试,这将帮助他们在调试前理解被测类的逻辑
- 良好的命名规范:测试用例的变量和方法都要有明确的含义。比如,将预期结果保存到 expectedFoo 变量而不是只保存到 foo。如果要测试很多复合结果,可使用组合变量名称诸如:inputValue_NotNull, inputValue_ZeroData, inputValue_PastDate等等。再次测试时应该像其他代码一样,便于人们理解
- 把断言从行为中分离出来:你的断言应该用来检验结果,而不是执行逻辑操作的
- 使用具体的输入:不要使用任何的自动化测试数据来输入,像 date() 这些产生的数据会引入差异
- 把测试类分类,放在不同的地方:从逻辑的角度看,当没有错误指向特定的问题时这更容易去查找
- 好的测试都是一些独立的测试类:你应该让测试类与其他的测试、环境设置等没有任何依赖。这利于创建多个测试点
- 不要包含私有的方法:他们都是一些具体的实现,是被封装起来的,并不是 API,不应该包含在单元测试里
- 不要连接数据库或者数据源:如果测试中需要连接数据库操作,必须使用 mock,每个新的测试方法都自主引导到临时数据库
- 测试用例不要连接网络资源:测试某个方法时无法确保第三方诸如网络和设备的有效性(使用mocks)
- 一个测试不要超过一个模拟(mock对象):我们努力去消除错误和不一致性
- 单元测试不是集成测试:如果你想测试结果,不要使用单元测试
- 测试必须具有确定性:你需要一个确定的预测结果,所以,如果有时候测试通过了,但是不意味着完成测试了
- 保持你的测试是幂等的:你应该能够运行你的测试多次而不改变它的输出结果,并且测试也不应该改变任何的数据或者添加任何东西。无论是运行一次还是一百万次,它的效果都应该是一样的
- 测试类一次仅测试一个类,测试方法一次仅测试一个方法:组织方法能够在问题出现时检测出来,并帮你确定测试依赖
- 测试用例要验证预期的异常:Java 中使用 @Test(expected = MyException.class)
- 不要使用你自己的测试类去测试第三方库的功能:大多数好的库都应该有它们自己的测试,如果没考虑用 mocks 去产生一致性的结果的话
- 限制规则:当在一些规则下写测试时,记住你的限制和它们(最小和最大)设置成最大的一致性
- 测试类不应该需要配置或者自定义安装:测试用例在任何情况下都可运行,并且不需要配置和人工干预
- 测试用例干净整洁:程序中不要有流程控制语句(switch,if 等)。一个好的测试用例处理顺序简单直接,准备数据,验证结果顺序,如有必要,使用子方法分解结构,让测试用例更加易读。如果是多场景,使用多个方法测试;一个测试用例的方法代码长度最多满屏,不要有滚动条,大概在 1 到 20 行左右。如果测试代码太长,考虑拆开成多个方法,以避免混在一起相互干扰
- 测试用例通过当前测试,并且易于改进:测试用例要能够支持代码的演变,如果很难维护或者代码太轻而不能细化,那就成了负担(很多人不写单元测试用例就是这个原因)
- 测试用例帮我验证了代码架构:如果你不能测试某个方法或者类,那么你的设计就不够灵活
- 测试用例运行速度快:慢的测试会把你拖垮,快的速度会鼓励你经常运行他们,它还能帮助你减少持续集成系统上的构建时间