Closed EthanLin-TWer closed 6 years ago
其实最近练习 TDD 并没感觉有重大突破,但跟多人交流沟通之后慢慢梳理出一些所以然来,对于一些问题在此尝试做简单的体系化归纳。真的有用,非常有用,感觉可以给烤鸭投稿了。
TDD 大法好。TDD 很有用。有意愿提升工作效率的同学快来看。
TDD 是一种提高个人工作效率的方法论/工具。英文为 Test Driven Development,意为测试驱动开发。本 issue 仅讨论其作为个人效率工具的这一维度,而不谈其作为团队敏捷实践的这一维度。原因是 TDD 与 TDD 推广乃是两回事。
值得注意的是,TDD 不是什么。TDD 不是效率工具以外的其他东西,你需要有其他多种多样的工具配合来发挥出它最大的威力,比如整洁代码的知识、设计知识、领域建模知识、领域特定(如前端框架、React、Vue 等)的知识等。
有。什么用。提高效率。提高多少效率。第三方估计30%左右。那为啥我身边没人在用。可能多数人适应低效工作,没啥改变的意愿。我用了,没觉得提高多少。你要么用错了,要么缺少练习。有兴趣就往下看。
所以这个东西,说是自傲也好,自暴自弃也好,并无所谓。关键是不要浪费时间吵闹,随时开始才是王道。不舒适随时右上角便是。
是快速反馈和自动化的回归测试。
不少同学可能会将 TDD 等同于测试,做 TDD 等于写测试,其实不然。关于测试其实主要就是三个问题:
第一个问题,肯定需要有测试,软件行业发展十几年,需要测试已经是个政治正确的问题了,不需要多加讨论;第二个问题,是测试策略应该回答的问题,后面讲;测试先行还是后行,其实才是要不要 TDD 的问题。
为什么一定要测试呢?道理十分简单:没有测试就没办法重构,没有重构基本就只能看着代码腐化。所以 TDD 的自动化的回归测试这层作用,本质上是测试的作用,作为一个长久运行的项目,一定要有测试,否则就只能看着代码腐化。
快速反馈回答的是测试先行还是后行这个问题。测试先行就是 TDD 的方式,它能给你带来快速的反馈。测试后行也行,只是一定程度上要失去这层优势。
后续学习 TDD、应用 TDD、对 TDD 实践进行裁剪时,请牢记这两个指导思想:快速反馈、自动化的回归测试。
很多已有入门的同学对 TDD 的了解可能最早的是口号式的「红绿循环」,这种理解欠缺了 TDD 很重要的一环前置条件:任务分解。任务分解又是什么东西?这么讲,TDD 的完整流程是这样的:
可以看到,我们平时讲的红绿循环,已经是这套工作流中的最后一步了。没有前面做任务分解的铺垫,你就得不到这个测试用例的列表,你的 TDD 也就是无本之木——要么少场景,要么多实现不需要的需求。这可能是我们容易犯的第一个错误。
我们有时编程效率为啥不高呢?总结起来,无非有这么一些因素:
然后再来看看 TDD 这套工作流为什么能帮助你提高这些方面的效率:
。。。突然觉得空讲这个东西非常枯燥。求反馈啊。
好,然后为什么你会发现这套方法初期做起来非常难?因为这里面许多关节都有一些更细节的技巧,你也需要多加训练才能运用自如,比如说下面这些。但最重要的还是一点思维上的转变,相对于问「我要实现什么什么」,你应该时时以验收的角度问「我如何自动验收这条子任务」「这条测试用例的输入输出和 API 应该是什么」?这些问题,强迫你去思考验收、思考 API,因此(也许)熊节也因此称 TDD 为一种「设计方法」。
上面讲的都是完整的方法论,实际上在实践的过程中,你完全可以根据环境、资源、代码库现状、具体代码等因素来对 TDD 进行裁剪,以使你进一步解放效率,更快更稳撸出高质量的代码。请紧紧抓住一个核心:作为效率工具的 TDD,其一切裁剪的核心都围绕提升反馈速度这个点展开。
是不是每个用例我都要用自动化测试来验收它?比如前端 UI 也要吗?比如前端 CSS 也要 TDD 吗?感觉很难啊,就像把实现抄了一遍,神经病。
是不是每个用例都要验收?是。是不是每个用例都要用自动化测试来验收?不是。还是两个维度:从反馈速度讲,UI 的东西通过 hot-reload +肉眼查看的方式,反馈速度已经很快,不需要画蛇添足使用测试来验收它,人肉验收已经足够可靠;从自动化的回归测试讲,自然就无法回归。但这个问题要看项目的测试策略对 UI 测试是什么观点,可以采用不回归或后补测试的方式来满足。
可以确定的是,对 UI 和 CSS 进行测试驱动,在 hot-reload+人眼测试这么快的前提下,一定是错误的玩法。
你说测试策略是什么?对了,是不是除了 UI 和 CSS 以外,每一行代码我都要写自动化测试来验收它?不可能啊。
TDD 神教的口号是,没有一个失败的测试,你就不应该写任何实现代码。因为你写了也没法验收。
测试策略要回答一个问题:应用中的每个层级每个部分应该测多详细,由谁来测。我们当然希望100%的覆盖率,然而根本不可能,所以我们就转而使用策略性的测试方法:对价值最大的、逻辑复杂的代码进行重点覆盖测试,对带来很多人力测试成本的层级进行重点覆盖测试,对其他简单的、很少出错的、信心较高的代码仅进行选择性测试。它本质是个成本-收益的平衡问题。
所以回到上一个问题,你的测试策略中要投入多大资源去测试 UI,取决于你们 UI 回归问题多不多,UI 测试成本高不高(目前看来主要取决于工具 diff 的成本和准确度)。如果 UI 不经常回归出问题,测试成本又高(我们现在的状况),测试策略中就直接放弃了这一部分。所以,你在完成样式类型任务的时候,使用人力验收就可以了。
「当需求发生改变的时候,除了要更改产品代码,还要维护测试代码,苦不堪言」。所以用 TDD 是不是其实更慢了?TDD 如何应对需求变更?
这是个伪问题。论述前半部分,你说「需求发生改变」时要维护测试代码因而慢,那么我们来看看这个「需求发生改变」指的是什么时候发生改变:
使用了 TDD/写了测试也没有足够的信心重构啊?写了感觉也很虚吗?
使用了 TDD 的单元能够重构。如果是系统级别的重构没有信心,那是系统级别的问题(比如类型系统、AT 测试覆盖、测试没写好等);但不写测试连单元都不敢重构。不写更虚噻。
个人项目也要使用 TDD 吗?
好问题。根据经验,上一个严格 TDD 的个人项目,一个月后就完全用不到了。在「个人项目」这个上下文下,它与工程级项目的不同在于,它追求更快出结果,更快验证价值,以及它的维护周期不一定会很长。所以我认为 TDD 中自动化的回归测试这一维,也可以适当地放弃,而以快速反馈为主线。如果我再起一个个人项目,我的 TDD 策略会是这样的:
TDD 和测试是维护质量的唯一方式吗?
首先,好的测试是重构的唯一保障,而重构是提升代码质量的重要方法(如果你写的测试都测的实现,导致一重构测试就挂,那是说明任务分解没做好,测试没写好)。但我觉得,不仅仅测试对代码质量有贡献,恰当的架构同样对质量有贡献。如果一个架构对各个层次抽象恰当,这种架构上的简单性将大大降低维护成本,因为代码十分直观。因此,我认为一个合适的代码库及其质量,应该是由恰当的架构、测试策略和好的测试支撑起来的:
TDD 一定要测试先行吗?
不是。依然如前,要紧紧抓住「反馈速度」这个核心进行裁剪。你比如写代码写 high 了一口气实现了好几个测试用例,你比如一些实现是类似的你先 copy 实现再补测试用例,那都是可以的,只要你依然可以产出高质量的测试用例(仅对输入输出敏感)等。前提是你对 TDD 这项技术已经非常熟悉了。并且它的妙处在于,不需要你提前做任何决定,也因此没有任何后悔的成本。它的一个直接结果就是,你再也不害怕做错了。如果你发现,你的步子迈大了,或者中午刚睡醒脑子不够灵光跟不上,一大步测试就挂一片,那么没关系,你只要 gco . 然后再恢复小步的步子,慢慢一个用例一个用例前进,都是可以并且是被鼓励的。
gco .
扎爷说,高手是可以在业务的验收视角和技术的实现视角不断切换的。带着验收的视角先写实现,我完全也可以补出优秀的测试用例。所以这个先后对于高手来说其实并不重要。更妙的是,你不需要成为了高手才可以这么做,你现在就可以尝试——当发现大步了,马上小步一些就可以了。
TDD 是你手中最重要的剑⚔,应时时跟它磨合调整,形成你们自己独特的节奏感和默契哟。
还有啥我忘了的?
的最后。求反馈。
另外,其实少说了个后置的 PDCA,其实就是反馈环,要通过 check - feedback 的方式不断检视、刻意练习自己的某些短板,以达到快速提高的目的。
这些关节清楚以后,其实你是可以自己去调节、随时裁剪实践的,并且仍然能够达到高反馈速度、高质量的产出。最近还有一点收获,就是方法之间往往不是互斥,多种方法互相借鉴使用往往可以取得非常强力的效果,所以不用执着于唯一的正确答案,而是多种方案多种方法论都可以交替使用验证。例子有比如:推荐用还是不用 mock(根据准备成本决定,当然是有时 mock 有时不 mock)、是不是一定要先写测试(如前面所述,当然是看经验精力等实时裁剪)等。
https://blog.linesh.tw/#/post/2018-06-04-tdd-series-end-the-big-picture
其实最近练习 TDD 并没感觉有重大突破,但跟多人交流沟通之后慢慢梳理出一些所以然来,对于一些问题在此尝试做简单的体系化归纳。真的有用,非常有用,感觉可以给烤鸭投稿了。
目录
精神宗旨
TDD 大法好。TDD 很有用。有意愿提升工作效率的同学快来看。
TDD 是什么
TDD 是一种提高个人工作效率的方法论/工具。英文为 Test Driven Development,意为测试驱动开发。本 issue 仅讨论其作为个人效率工具的这一维度,而不谈其作为团队敏捷实践的这一维度。原因是 TDD 与 TDD 推广乃是两回事。
值得注意的是,TDD 不是什么。TDD 不是效率工具以外的其他东西,你需要有其他多种多样的工具配合来发挥出它最大的威力,比如整洁代码的知识、设计知识、领域建模知识、领域特定(如前端框架、React、Vue 等)的知识等。
TDD 真的有用吗
有。什么用。提高效率。提高多少效率。第三方估计30%左右。那为啥我身边没人在用。可能多数人适应低效工作,没啥改变的意愿。我用了,没觉得提高多少。你要么用错了,要么缺少练习。有兴趣就往下看。
所以这个东西,说是自傲也好,自暴自弃也好,并无所谓。关键是不要浪费时间吵闹,随时开始才是王道。不舒适随时右上角便是。
TDD 两大核心
是快速反馈和自动化的回归测试。
不少同学可能会将 TDD 等同于测试,做 TDD 等于写测试,其实不然。关于测试其实主要就是三个问题:
第一个问题,肯定需要有测试,软件行业发展十几年,需要测试已经是个政治正确的问题了,不需要多加讨论;第二个问题,是测试策略应该回答的问题,后面讲;测试先行还是后行,其实才是要不要 TDD 的问题。
为什么一定要测试呢?道理十分简单:没有测试就没办法重构,没有重构基本就只能看着代码腐化。所以 TDD 的自动化的回归测试这层作用,本质上是测试的作用,作为一个长久运行的项目,一定要有测试,否则就只能看着代码腐化。
快速反馈回答的是测试先行还是后行这个问题。测试先行就是 TDD 的方式,它能给你带来快速的反馈。测试后行也行,只是一定程度上要失去这层优势。
后续学习 TDD、应用 TDD、对 TDD 实践进行裁剪时,请牢记这两个指导思想:快速反馈、自动化的回归测试。
TDD 完整实践流程
很多已有入门的同学对 TDD 的了解可能最早的是口号式的「红绿循环」,这种理解欠缺了 TDD 很重要的一环前置条件:任务分解。任务分解又是什么东西?这么讲,TDD 的完整流程是这样的:
可以看到,我们平时讲的红绿循环,已经是这套工作流中的最后一步了。没有前面做任务分解的铺垫,你就得不到这个测试用例的列表,你的 TDD 也就是无本之木——要么少场景,要么多实现不需要的需求。这可能是我们容易犯的第一个错误。
我们有时编程效率为啥不高呢?总结起来,无非有这么一些因素:
然后再来看看 TDD 这套工作流为什么能帮助你提高这些方面的效率:
。。。突然觉得空讲这个东西非常枯燥。求反馈啊。
好,然后为什么你会发现这套方法初期做起来非常难?因为这里面许多关节都有一些更细节的技巧,你也需要多加训练才能运用自如,比如说下面这些。但最重要的还是一点思维上的转变,相对于问「我要实现什么什么」,你应该时时以验收的角度问「我如何自动验收这条子任务」「这条测试用例的输入输出和 API 应该是什么」?这些问题,强迫你去思考验收、思考 API,因此(也许)熊节也因此称 TDD 为一种「设计方法」。
Q&A
上面讲的都是完整的方法论,实际上在实践的过程中,你完全可以根据环境、资源、代码库现状、具体代码等因素来对 TDD 进行裁剪,以使你进一步解放效率,更快更稳撸出高质量的代码。请紧紧抓住一个核心:作为效率工具的 TDD,其一切裁剪的核心都围绕提升反馈速度这个点展开。
是不是每个用例都要验收?是。是不是每个用例都要用自动化测试来验收?不是。还是两个维度:从反馈速度讲,UI 的东西通过 hot-reload +肉眼查看的方式,反馈速度已经很快,不需要画蛇添足使用测试来验收它,人肉验收已经足够可靠;从自动化的回归测试讲,自然就无法回归。但这个问题要看项目的测试策略对 UI 测试是什么观点,可以采用不回归或后补测试的方式来满足。
可以确定的是,对 UI 和 CSS 进行测试驱动,在 hot-reload+人眼测试这么快的前提下,一定是错误的玩法。
TDD 神教的口号是,没有一个失败的测试,你就不应该写任何实现代码。因为你写了也没法验收。
测试策略要回答一个问题:应用中的每个层级每个部分应该测多详细,由谁来测。我们当然希望100%的覆盖率,然而根本不可能,所以我们就转而使用策略性的测试方法:对价值最大的、逻辑复杂的代码进行重点覆盖测试,对带来很多人力测试成本的层级进行重点覆盖测试,对其他简单的、很少出错的、信心较高的代码仅进行选择性测试。它本质是个成本-收益的平衡问题。
所以回到上一个问题,你的测试策略中要投入多大资源去测试 UI,取决于你们 UI 回归问题多不多,UI 测试成本高不高(目前看来主要取决于工具 diff 的成本和准确度)。如果 UI 不经常回归出问题,测试成本又高(我们现在的状况),测试策略中就直接放弃了这一部分。所以,你在完成样式类型任务的时候,使用人力验收就可以了。
这是个伪问题。论述前半部分,你说「需求发生改变」时要维护测试代码因而慢,那么我们来看看这个「需求发生改变」指的是什么时候发生改变:
使用了 TDD 的单元能够重构。如果是系统级别的重构没有信心,那是系统级别的问题(比如类型系统、AT 测试覆盖、测试没写好等);但不写测试连单元都不敢重构。不写更虚噻。
好问题。根据经验,上一个严格 TDD 的个人项目,一个月后就完全用不到了。在「个人项目」这个上下文下,它与工程级项目的不同在于,它追求更快出结果,更快验证价值,以及它的维护周期不一定会很长。所以我认为 TDD 中自动化的回归测试这一维,也可以适当地放弃,而以快速反馈为主线。如果我再起一个个人项目,我的 TDD 策略会是这样的:
首先,好的测试是重构的唯一保障,而重构是提升代码质量的重要方法(如果你写的测试都测的实现,导致一重构测试就挂,那是说明任务分解没做好,测试没写好)。但我觉得,不仅仅测试对代码质量有贡献,恰当的架构同样对质量有贡献。如果一个架构对各个层次抽象恰当,这种架构上的简单性将大大降低维护成本,因为代码十分直观。因此,我认为一个合适的代码库及其质量,应该是由恰当的架构、测试策略和好的测试支撑起来的:
不是。依然如前,要紧紧抓住「反馈速度」这个核心进行裁剪。你比如写代码写 high 了一口气实现了好几个测试用例,你比如一些实现是类似的你先 copy 实现再补测试用例,那都是可以的,只要你依然可以产出高质量的测试用例(仅对输入输出敏感)等。前提是你对 TDD 这项技术已经非常熟悉了。并且它的妙处在于,不需要你提前做任何决定,也因此没有任何后悔的成本。它的一个直接结果就是,你再也不害怕做错了。如果你发现,你的步子迈大了,或者中午刚睡醒脑子不够灵光跟不上,一大步测试就挂一片,那么没关系,你只要
gco .
然后再恢复小步的步子,慢慢一个用例一个用例前进,都是可以并且是被鼓励的。扎爷说,高手是可以在业务的验收视角和技术的实现视角不断切换的。带着验收的视角先写实现,我完全也可以补出优秀的测试用例。所以这个先后对于高手来说其实并不重要。更妙的是,你不需要成为了高手才可以这么做,你现在就可以尝试——当发现大步了,马上小步一些就可以了。
TDD 是你手中最重要的剑⚔,应时时跟它磨合调整,形成你们自己独特的节奏感和默契哟。
最后
的最后。求反馈。
另外,其实少说了个后置的 PDCA,其实就是反馈环,要通过 check - feedback 的方式不断检视、刻意练习自己的某些短板,以达到快速提高的目的。
这些关节清楚以后,其实你是可以自己去调节、随时裁剪实践的,并且仍然能够达到高反馈速度、高质量的产出。最近还有一点收获,就是方法之间往往不是互斥,多种方法互相借鉴使用往往可以取得非常强力的效果,所以不用执着于唯一的正确答案,而是多种方案多种方法论都可以交替使用验证。例子有比如:推荐用还是不用 mock(根据准备成本决定,当然是有时 mock 有时不 mock)、是不是一定要先写测试(如前面所述,当然是看经验精力等实时裁剪)等。