定律一:在编写不能通过的单元测试前,不可编写生产代码。
解释:
这条定律的意思是,你不能直接编写实现功能的生产代码,而是在开始之前需要先编写一个单元测试来验证这个功能。这个单元测试应该是你期望生产代码通过的测试。只有在有了这个测试之后,你才能开始编写生产代码。
目的:
- 确保每一段生产代码都有明确的测试用例。
- 强制开发者先考虑需求和设计。
- 避免无测试的代码积累,确保代码可测试性。
定律二:只可编写刚好无法通过的单元测试,不能编译也算不过。
解释:
这条定律的意思是,你在编写单元测试时,只能编写那些因为生产代码尚未实现或部分实现而无法通过的测试。这里“无法通过”包括编译错误的情况。这意味着测试应该是为了验证当前还没有实现的功能或行为。
目的:
- 保持测试的专注性,只测试当前未实现或部分实现的功能。
- 避免编写过多或过于复杂的测试,确保测试覆盖率和测试代码的质量。
- 确保测试和实现同步进行,不会过早地编写多余的测试。
定律三:只可编写刚好足以通过当前失败测试的生产代码。
解释:
这条定律的意思是,你应该只编写刚好能让当前失败的测试通过的生产代码,不要超出这个范围。生产代码应仅满足测试的需求,而不是实现更多的功能。
目的:
- 保持生产代码的简洁和专注,只实现当前需求。
- 避免过度设计和提前优化。
- 逐步迭代开发,通过小步快跑的方式保证代码质量和稳定性。
实践中的应用
在实际开发中,这些定律的应用可以归纳为一个循环过程:
编写一个失败的单元测试(红):
根据需求编写一个新的单元测试,此时因为没有实现相关功能,测试会失败。
编写生产代码让测试通过(绿):
编写最小量的生产代码,刚好让这个测试通过,不要做额外的实现。
重构(重构):
清理和优化代码,确保代码质量,同时保证所有测试仍然通过。
通过遵循这些定律,开发者可以逐步实现功能,确保每一段代码都有对应的测试覆盖,从而提高代码质量和可维护性。
构造-操作-检验(BUILD-OPERATE-CHECK)模式
构造-操作-检验(BUILD-OPERATE-CHECK)模式是编写测试用例的一个经典模式,它分为三个主要环节:
-
构建测试数据(Build)
- 设置初始状态或准备输入数据。确保测试有一个清晰和可预见的起点。
-
操作或处理测试数据(Operate)
- 调用被测试的代码,用构建的数据进行操作。执行你想测试的方法或功能。
-
校验真实代码的输出结果(Check)
- 验证操作的结果,检查输出是否符合预期。断言测试结果以确保功能正确。
测试的5条(FIRST)准则
为了确保测试用例的质量和有效性,可以遵循以下5条准则,简称FIRST:
-
快速(Fast)
- 测试应该快速运行,能够及时反馈出业务代码的问题。快速的测试可以频繁运行,确保及时发现问题。
- 例如,单元测试通常应该在几秒内完成。
-
独立(Independent)
- 每个测试应该是独立的,彼此之间不应该有依赖关系。这样可以避免因为一个测试的失败影响到其他测试。
- 例如,测试A不应该依赖测试B的运行结果。
-
可重复(Repeatable)
- 测试应该能够在任何环境上重复运行并获得相同的结果。不应该受到环境、网络、时间等外部因素的影响。
- 例如,在本地开发环境和CI/CD环境中都能通过。
-
自我验证(Self-Validating)
- 测试应该有明确的bool输出(通过或失败)。测试结果应该是明确的,而不是模糊的或需要人工检查。
- 例如,使用断言来验证测试结果是否符合预期。
-
及时(Timely)
- 测试应该在编写代码的过程中或之后及时编写,而不是事后补充。这有助于捕获早期的错误并引导设计。
- 例如,在开发新功能时同时编写对应的单元测试。