测试驱动开发
主要内容:
- 什么是TDD
- 实现流程
- 开发风格
- 优点
- 是否需要TDD
什么是TDD
测试驱动开发(test-driven development, TDD
)是一种敏捷开发模型的开发流程,首先编写测试用例来理清开发需求,之后修改代码以通过测试,最后重构代码,不断重复这个开发周期。TDD
通过测试用例来具体化开发需求,不仅有利于需求的实现,也有利于软件的鲁棒性
实现流程
添加测试(Add a test)
在
TDD
中,每次添加新功能都需要先编写测试用例。可以修改原有测试代码或者添加新的测试代码,其目的是让开发者更加理解和明确新功能的要求和规范,同时编写的测试应该覆盖所有的意外情况运行所有测试,是否新测试失败(Run all tests and see if the new test fails)
执行这一步骤是为了验证修改后的测试代码是否符合原先的测试要求,同时验证新测试是否因为预期原因而失败,保证测试的可靠性
编写代码(Writing the code)
修改代码以确保测试通过。这个步骤过程中不关注代码可读性,同时不能编写超出测试检查功能之外的代码
运行测试(Run tests)
确认所有测试是否通过。这一步骤确保编写的代码符合新测试的要求,并且没有破坏(
break
)或降低(degrade
)已有测试,如果没有成功,重复第三步重构(Refactor code)
修改代码已保证代码可读性,确保对象(
Object
)、类(Class
)、模块(Module
)、变量(Variable
)和方法(Method
)名能够清晰的表明其当前目的和作用重复(Repeat)
重复上述循环以推动功能实现。每个周期的修改部分应该尽可能小,最好仅有
1-10
处编辑位置。如果新代码无法快速满足新测试,可以先恢复(revert
)修改代码,重新结构化需求和测试
上述实现流程设定了开发节点,配合版本管理工具和持续集成工具能够更好的帮助开发
开发风格
保持代码尽可能简洁和简单,遵循以下3条原则:
- Keep it simple, stupid(KISS)
- You aren't gonna need it(YANGI)
- Fake it until you make it
小单元开发
每个开发周期尽可能涉及较少的类或者模块,有以下好处:
- 减少调试工作量 - 当检测到测试失败时,较小的单元有助于跟踪错误
- 自我文档测试 - 小型测试用例更容易阅读和理解
测试结构
保持统一的测试结构有助于提高可读性以及理解执行流程,常用的测试用例结构如下:
- 设置(
setup
):将被测单元(the Unit Under Test, UUT
)或整个测试系统置于运行测试所需的状态,也就是设置初始状态 - 执行(
execution
):触发或驱动UUT
执行目标行为,并捕获所有输出,比如返回值和输出参数 - 验证(
validation
):确保测试结果正确。这些结果可能包括执行期间捕获的显式输出或UUT
中的状态更改 - 清理(
cleanup
):将UUT
或整个测试系统恢复到预测试状态。此恢复允许在此之后立即执行另一个测试
环境分离
开发过程中保持测试和生产代码分离,确保发布版本中不包含测试代码
优点
测试优先能够保证接下来的编写代码环节专注于可检验性(testability
),同时确保每次添加新功能都会实现测试用例,有助于更深和更快的理解产品需求,保持对产品质量的关注。如果在功能完成之后再编写测试用例,经常会专注于下一个新功能而忘记测试(有道理)
是否需要TDD
网上有一些讨论:TDD(测试驱动开发)是否已死?,关于是否需要TDD
编程
就我个人而言,虽然只是刚开始接触,但是之前的coding
经历让我明确了测试实现的必要性,学习TDD
流程会有一种豁然开朗的感觉,不管咋样,先尝试一段时间看看再说