什么是TDD?
TDD 是敏捷开发中的一项核心实践和技术,也是一种设计方法论。TDD的原理是在开发功能代码之前,先编写单元测试用例代码,测试代码确定需要编写什么产品代码。
为什么要 TDD?
TDD 的好处
- 降低开发者负担 通过明确的流程,让我们一次只关注一个点,思维负担更小。
- 保护网 TDD 的好处是覆盖完全的单元测试,对产品代码提供了一个保护网,让我们可以轻松地迎接需求变化或改善代码的设计。 所以如果你的项目需求稳定,一次性做完,后续没有任何改动的话,能享受到 TDD 的好处就比较少了。
- 提前澄清需求 先写测试可以帮助我们去思考需求,并提前澄清需求细节,而不是代码写到一半才发现不明确的需求。
- 快速反馈 有很多人说 TDD 时,我的代码量增加了,所以开发效率降低了。 但是,如果没有单元测试,你就要手工测试,你要花很多时间去准备数据,启动应用,跳转界面等,反馈是很慢的。准确说,快速反馈是单元测试的好处。
TDD重要的不是测试代码本身,是解决问题的思维, TDD驱使我们以结果为导向,使得我们简化设计, 注重交付价值流的稳定叠加。
TDD的终极目标是产出干净且可用的代码
TDD要咋么做?
TDD 的三原则
- 没有测试之前不要写任何功能代码
- 一次只写一个刚好失败的测试,作为新加功能的描述
- 不写任何多余的产品代码,除⾮它刚好能让失败的测试通过
同时TDD也要要遵循测试的FIRST原则
- F(Fast):测试要能快速运行
- I(Isolate):测试用例要独立,不能相互依赖
- R(Repeatable):测试要可以重复运行
- S(Self-verifying):测试会自己检查产出
- T(Timely):测试要及时做,与写代码紧密相连
测试用例规范
为了保证TDD的实施效率, 我们在实操前必需先熟悉一下测试用例的编写规范, 这样才能保证我们的测试标准化, 从而为后期自动化测试基础.
golang测试用例规范
案例演示
用户手机号密码登陆服务, 详见附件列表
具体过程详见: TDD案例实战
自动化集成
- 接口测试自动化可以直接沿用Yapi的自动化测试方案go test -coverprofile=c.out ./... go tool cover -html=c.out -o coverage.html
- 单元测试则可以在流水线上提前执行
常见问题
为什么很多人做 TDD 都做不起来?
- 不会合理拆分任务 TDD 之前要拆分任务,把一个大需求拆成多个小需求。
- 不会写测试 什么是有效的单元测试,有很多人写测试,连到底在测什么都不清楚,也可能连断言都没有,通过控制台输出,肉眼对比来验证。 好的单元测试应该符合简单, 速度快, 包含断言且可以重复执行
- 不会写刚好的实现 很多人写实现的时候无法专注当前需求,一不小心就把其他需求也实现了,就破坏了节奏感。 实现的时候不会小步快走。
- 不会重构 不懂什么是 Clean Code,看不出 Smell,没有及时重构,等想要重构时已经难以下手了。 不知道用合适的「手法」消除 Smell。
- 基础设施落后 对于特定技术栈,没有把单元测试基础设施搭建好,导致写测试时无法专注在测试用例上。 TDD (Test-driven development) 是一种借助自动化测试,并充分发挥其优势的开发模式。如果基础设施不想, 那么TDD反而适得其反.
为什么一定要先写测试,后补测试行不行?
行,但是要写完实现后,马上写测试,用测试来验证实现, 如果测试先行,使用意图驱动编程减少返工
测试代码是否会成为维护的负担?
维护时也遵循 TDD 流程,先修改测试代码成需求变更后的样子,让测试失败,再修改产品代码使其通过。这样你就不是在维护测试用例,而是在利用测试用例。
为什么测试代码要很简单?
当测试代码足够简单时,如果一个测试失败了,就有足够信心断定一定是产品代码的问题。
什么时候不适合 TDD?
如果你是做探索性的技术研究(Spike),不需要长期维护,而且测试基础设施搭建成本很高,那还是手工测试吧。
另外还有「可测试性极差的遗留系统」和「使用测试不友好的技术栈」的系统,做 TDD 可能得不偿失。
参考文献
《 TDD 開發五步驟,帶你實戰 Test-Driven Development 範例 》
《测试驱动开发(TDD)实践与技巧 》
《TDD案例-重复字符串和冒泡排序 》