- 单元测试(UT)概念
- 单元测试是一种实践,要求我们对每个隔离的小型代码单元进行测试。
- 单元通常是方法,但有些情况下,整个类乃至整个应用程序都可视为单元。
- 如果特定方法的单元测试跨越了该单元的边界,它将变成集成测试。
- 为何要进行单元测试
- 单元测试并非要取代其他类型的测试,而只是缩小其他测试的范围。
- 单元测试的编写比其他任何类型的测试都更容易、更快捷,从而能够降低成本、缩短上市时间。
- 对于重构所需的代码覆盖率来说,只有单元测试能够达到要求的细致程度。
- 单元测试并非要取代其他类型的测试,而只是缩小其他测试的范围。
- 单元测试和其他测试的差别
单元测试 | 功能测试和验收测试 | 集成测试 | |
职责 | 对小型功能单元进行检查 | 核实整个应用程序像预期的那样工作 | 核实各个单元、模块、应用程序乃至系统被妥善地集成在一起 |
目标 | 检查代码的内部质量 | 保整个系统在客户或用户看来能够正常工作 | |
数量 | 最大 | 中 | 最少 |
- TDD中的单元测试和传统的单元测试区别
- 编写时机:传统做法是在实现代码完成后编写单元测试;而TDD中的顺序相反——先编写测试。
- 目标:未使用TDD的情况下,单元测试用于验证既有代码;而在TDD中,应将单元测试作为驱动开发和设计的动力,它们定义最小可能单元的行为,指定有待实现的微型需求。
- TDD迫使我们详细地考虑需求和设计、编写整洁而可行的代码,以及创建可执行的需求并频繁重构。
- 编写时机:传统做法是在实现代码完成后编写单元测试;而TDD中的顺序相反——先编写测试。
- 如果在15秒内能够运行所有测试,就没有必要运行部分测试。
- 另一方面,如果测试的运行速度很慢,通常昭示着没有将外部依赖隔离。不管测试运行速度慢的原因是什么,都不能将运行部分测试作为解决方案,而应去修复问题。
- 大多数UT新手都会落入这样的陷阱,即指定方法的结果时,牵涉到它使用的方法、类和库的内部工作原理。这种做法在很多层面上都存在问题。
例如:
修改前
public void givenEastWhenMoveForwardThenXIncreases() {
ship.getLocation().setDirection(Direction.EAST);
ship.moveForward();
assertEquals(ship.getLocation().getPoint().getX(), 22);
}
这种做法存在的另一个问题是,如果外部代码发生变化,将需要修改很多规范;而理想情况下,单元被修改时,应只需修改与之直接相关的规范。
修改后:
public void whenMoveForwardThenForward() {
Location expected = location.copy();
expected.forward();
ship.moveForward();
assertEquals(ship.getLocation(), expected);
}
规范不仅用于验证代码,还被用作可执行的文档,最重要的是,它们还被用作思考和设计方式。
前述修改后的规范更清晰地指出了其意图:应在Ship类中创建方法moveForward,并确保这个方法调用了location.forward。
简约是关键,而重构通常是必须的。
仅当所有测试都通过后才重构。这样做的优点:重构是安全的。
很多情况下,我们编写规范时都没有验证最终结果是否正确,而检查待实现的方法是否调用了辅助类的正确方法。