浅谈测试驱动开发TDD之争

前言

在历史上有很多精彩绝伦的神仙打架,比如数学界的牛顿和莱布尼茨关于微积分的旷世之争;比如量子物理中的爱因斯坦和波尔的紫禁之巅;比如足球里的梅西和C罗的旗鼓相当难分高下;又比如滴滴和快滴之间触目惊心的烧钱大战……而在软件行业中,也同样有神仙打架的名场面,那就不得不提的是2014年的那场——测试驱动开发(TDD)之争。

比赛的红方是David Heinemeier Hansson,蓝方是Kent Beck。David Heinemeier Hansson 由于名字较长简写成DHH,Ruby on Rails 正是出自于DHH之手。而这场打架还加入了“裁判”员——Martin Fowler,在比赛中Martin Fowler记录了红蓝双方的每一次组合拳、上勾拳、侧踹、抱摔……总结如下:

红方DHH观点

1.许多推动TDD的开发人员会让你觉得:如果你不使用TDD的话,你的代码就是肮脏的。

2.由单元测试开始驱动你的设计并不是一个好的主意。

3.TDD的概念“测试必须够快”是目光短浅的。

4.对TDD的依赖会导致彻底忘记系统测试。

5.关注并且只关注单元模块并不能有助于创建一套完美的系统。

6.100%的覆盖率是愚蠢的。

7.程序员希望软件是一门科学,可是它并不是。它更像是创造性的写作活动。

8.优秀的软件并不像工程学那样,它更像写作。清楚简洁的写作要优于复杂晦涩的写作。

9.清晰是有好处的,好到应该将清晰性作为第一目标,而非测试覆盖度或者测试速度。

10.成为一名优秀的开发人员就像成为一名优秀的作家一样困难。

11.就像写作一样,成为优秀的程序员的办法就是以清晰为目标从而大量编写软件、大量阅读软件。

蓝方Kent Beck观点

DHH已将TDD委托给历史垃圾堆。我很难过,不是因为我就把它从历史的垃圾堆中拯救出来,而是因为现在我需要雇佣新技术来帮助我解决编程过程中的许多问题:

1.过度工程化。我倾向于“投入”我“知道”我“将需要”的功能。使一个红色的测试变为绿色(以及未来的测试列表)有助于我实现足够的功能。我需要找到一个新的方法来保持专注。

2.API反馈。我需要找到一种新的方法来获得关于我的API决策的快速反馈。

3.逻辑错误。我需要找到一种新的方法来抓住那些我很容易犯的讨厌的测试错误。

4.文档。我需要找到一种新的方式来传达我对api的期望,并记录我在开发过程中的想法。

5.感到不知所措。我真的会怀念如何使用TDD,即使我无法想象一个实现,我几乎总能想出如何编写测试。我需要找到一个新的方法,以便下一步上山。

6.将接口与实现思想分离。我倾向于用实现推测来污染API设计决策。我需要找到一种新的方法来分离这两个层次的思维,同时在它们之间提供快速的反馈。

7.协议。我需要找到一个新的方法,精确地与一个编程伙伴关于我正在解决的问题。

8.焦虑。也许我最怀念的是TDD给我的瞬间“一切都好吗?”按钮。

9.我相信我会找到其他方法来解决这些问题。及时。疼痛会减轻的。再见TDD,老朋友。

神仙打架不亏是神仙打架,从那以后业界关于测试驱动开发的观念也分成了两派。一派主要来源自像国内的一些互联网等项目中声音——需求的迭代和更新之快,要求公司或团队能快速交付有价值的产品,而TDD对于很多开发人员来说无疑是带来了繁重的工作压力和交付压力。甚至有人开玩笑话说:“ Deadline Driven Development 才是第一生产力 ”。

当然也有人力挺TDD,“TDD并没有死。很明显,既然它有这么这么多的支持者,它怎么可能会死呢? 这就像在问,设计模式死了吗?或者功能性自动化死了吗?不,它并没有死。而且它在将来任何时候都不会死亡。它将来可能会变成其他一些新的事物、甚至是一些更好的事物,但是它永远不会死亡。所以让我们跳过这一部分吧。”

关于测试驱动开发说了这么久,那么测试驱动开发到底是个啥呢?

测试驱动开发(TDD)是什么

测试驱动开发,英文全称Test-Driven Development,简称TDD,是一种不同于传统软件开发流程的新型的开发方法。 它要求在编写某个功能的代码之前先编写测试代码,然后只编写使测试通过的功能代码,通过测试来推动整个开发的进行。 这有助于编写简洁可用和高质量的代码,并加速开发过程。

Kent Beck:“测试驱动开发不是一种测试技术。它是一种分析技术、设计技术,更是一种组织所有开发活动的技术”。

分析技术: 体现在对问题域的分析,当问题还没有被分解成一个个可操作的任务时,分析技术就派上用场,例如需求分析、任务拆分和任务规划等,《实例化需求》这本书可以给予一定的帮助作用。

设计技术: 测试驱动代码的设计和功能的实现,然后驱动代码的再设计和重构,在持续细微的反馈中改善代码。

组织所有开发活动的技术: TDD 很好地组织了测试、开发和重构活动,但又不仅限于此,比如实施 TDD 的前置活动包括需求分析、任务拆分和规划活动,这使得 TDD 具有非常好的扩展性。

测试驱动开发(TDD)的目标

Kent Beck 在他的著作《Test-Driven Development》(见参考附录)一书中提到:“代码简洁可用这句言简意赅的话,正是 TDD 所追求的目标”。

对于如何保证“代码简洁可用”可以使用分而治之的方法,先达到“可用”目标,再追求“简洁”目标。

可用: 保证代码通过自动化测试。

代码简洁: 在不同阶段人们对简洁的理解程度也不一样,不过遵循的原则差不多,例如 OOD 的 SOLID 原则(详见参考附录),Kent Beck 的 Simple Design 原则(详见参考附录)等。

虽然有很多因素妨碍我们得到整洁的代码,甚至可用的代码,无需征求太多意见,只需要采用 TDD 的开发方式来驱动出简洁可用的代码。

测试驱动开发(TDD)的规则

在TDD 的过程中,需要遵循的三项原则:

1.在编写好失败的单元测试之前,不要写任何产品代码。

2.只要有一个单元测试失败了,就不要再写测试代码。无法通过编译也是一种失败。

3.产品代码恰好能够让当前失败的单元测试成功通过即可,不要多写。

测试驱动开发(TDD)的流程

测试驱动开发是一个过程,依赖于不断重复极短的开发周期,这个周期也称为“红灯-绿灯-重构”,如上图。简单的来说,基于TDD的三项原则,TDD的这种步骤(周期)如下:

1.添加一个小的测试

2.运行测试并查看失败

3.对测试进行微小的改动通过测试

4.运行所有测试并看到其通过

5.通过重构去掉重复部分

需要注意的是,不同阶段有不同的目的,他们需要不同的解决方案,前二个阶段需要很快地完成,以便知道新添加功能的状态。为了达成这个目的,可以通过任何手段,因为仅在这时才这样做,也是为了能快速完成好的设计。

测试驱动开发(TDD)的好处

TDD主要的好处主要包括了,确定性、重构代码、单元测试即文档。

确定性。TDD提升了单元测试的覆盖率,在每轮迭代产品都会新增代码,如果有一套覆盖率很高( 90% 或更高)的单元测试,那么只需执跑一遍测试用例,那么能成功交付的把握就会比较大。反之,如果覆盖率越低,越需要更多的人力去进行手动验证。 在 kent Beck的《测试驱动开发》举的例子中,正因有了TDD才有勇气和老板说我们可以做!这就是TDD最强大的地方,它让你拥有一套值得信赖的测试,打消你对修改代码的恐惧。

重构代码。Martin Flower在他的《重构》中也指出,完善的单元测试是他进行重构的基石,从TDD的流程可以看到,重构是TDD的一部分,运用TDD的同时也推动了代码的重构。

单元测试即文档。在软件行业里,人员的变动的很频繁的,如果要尽快熟悉某个模块的业务逻辑。看文档?程序员写的文章一般都不太容易看,而且文档经常会和代码不同步,代码修改了文档没跟着改的事情经常发生。看源码?看完也不一定知道为什么。如果这时候有一套非常完整的单元测试,那可能就是所有接手别人代码的程序员的福音。首先,代码不会撒谎,其次,测试用例明确告诉了你这个函数是做什么的,什么输入对应的都有什么预期输出。单元测试就是最好的底层文档,哪个专业人士不想提供这样一份文档呢?

此外,TDD还能够促成良好的代码设计。由于你先写测试代码,你会尽可能的让代码调用起来更加简单方便,这也就促使你去考虑如何更好的设计代码。以避免会出现一个函数里实现的功能过多,或者和其他代码过于耦合而无法测试的情况。

当然测试驱动开发除了好处以外,还有神仙打架中红方代表DHH所提出的一些问题。总结来看,关于TDD的争议可以大致从这几个方面来看,软件开发应该由什么来驱动,测试的速度和覆盖程度,以及设计思想层面等几方面。从 辩证统一的角度来看,事物有两个方面, TDD不一定能适用于所有的场景,同样TDD的局限性在某些场景下也不见得是对的,如果想要能更好的适用于自身,不仅要拿捏好度的问题还要以敏捷的思想来应对问题,比如不应该盲目的制定100%或0%的测试覆盖率,也不应该固化开发步骤而不顾实际情况。

所以,在最后的神仙打架中,Kent Beck也表达了David的论述可能会让TDD浴火重生、凤凰涅槃的观点,希望可以找到更加好的方法。但无论如何, 在我们实际工作中,不应该因为某些 观点成为我们接受或者拒绝它的理由。正所谓大道甚夷,而民好径,作为敏捷开发中的一项优秀实践来看,TDD只有在真正使用过后才能评价是否已死的问题。那么你在践行敏捷开发的时候,是否使用过TDD这种实践呢,又或是践行过其他一些敏捷开发的实践呢,有没有评测过你所在的项目中的敏捷开发的成熟度是如何的呢?

没有那就对了!华为云的DevCloud专业服务正是为此而生!

针对不同的岗位提供了评估的能力,让开发者可以对号入座,并基于你所在的岗位、技术得到客观、全面、系统的测评以及名师般的学习引导。快来访问专业服务平台,通过个人能力评估,看看自己是什么水平吧!

以上就是浅谈测试驱动开发TDD之争的详细内容,更多关于测试驱动开发TDD之争的资料请关注我们其它相关文章!

(0)

相关推荐

  • Nodejs学习笔记之测试驱动

    分享第二章,关于测试驱动.这里的测试主要针对Web后端的测试 -- 你为什么要写测试用例(即测试用例的完善是否是浪费时间),如何完善你的测试用例,代码设计如何简化测试用例的书写,以及一些后期的构想. 1. 你为什么要写测试用例 这个习惯通常会被认为是一种耽误开发进度的行为,你需要花费几乎和开发代码相同的时间来逐步完善你的测试用例.但是在开发过程中,在开发完成一段代码后如果负责任而不是说完全把问题交给测试人员去发现的话,这个时候通常都会去做一些手动的测试.例如: 在代码中执行某些方法,查看输出的值

  • 详尽讲述用Python的Django框架测试驱动开发的教程

    测试驱动开发(TDD)是一个迭代的开发周期,强调编写实际代码之前编写自动化测试. 这个过程很简单: 先编写测试. 查看测试失败的地方 编写足够的代码以使测试通过. 再次测试. 代码重构 . 重复以上操作. 为什么要用TDD? 使用TDD,你将学会把你的代码拆分成符合逻辑的,简单易懂的片段,这有助于确保代码的正确性. 这一点非常重要,因为做到下面这些事情是非常困难的: 在我们的脑中一次性处理所有复杂的问题. 了解何时从哪里开始着手解决问题. 在代码库的复杂度不断增长的同时不引入错误和bug:并且

  • python测试驱动开发实例

    本文实例讲述了python测试驱动开发的方法,分享给大家供大家参考.具体方法如下: import unittest from main import Sample class SampleTest(unittest.TestCase): def setUp(self): print "create a new Sample" self._sample = Sample("b64e5843ca7db8199c405be565fa7f57") def tearDown(

  • QUnit jQuery的TDD框架

    在讨论jQuery TDD之前,我们先来了解下什么才是一个标准的TDD框架.作为标准的TDD框架,必须满足这么几个要求: 1. 即使测试脚本出错了也要能继续运行接下来的脚本 2. 能够不依赖被测试代码写测试用例,即使代码没有实现也可以先写测试用例 3. 能够显示详细的错误信息和位置 4. 能够统计通过和未通过的用例的数量 5. 有专门的可视化界面用于统计和跟踪测试用例 6. 易于上手,通过一些简单的指导就可以马上开始写测试代码. 以上这些要求QUnit都做到了, 这也是我推荐QUnit的原因.

  • 利用Python如何实现数据驱动的接口自动化测试

    前言 大家在接口测试的过程中,很多时候会用到对CSV的读取操作,本文主要说明Python3对CSV的写入和读取.下面话不多说了,来一起看看详细的介绍吧. 1.需求 某API,GET方法,token,mobile,email三个参数 token为必填项 mobile,email 必填其中1项 mobile为手机号,email为email格式 2.方案 针对上面的API,在做接口测试时,需要的测试用例动辄会多达10+, 这个时候采用数据驱动的方式将共性的内容写入配置文件或许会更合适. 这里考虑把AP

  • Android开发笔记之:对实践TDD的一些建议说明

    最近部分采用了TDD的方法来开发一个模块,小有收获特此总结一下:1. TDD的基本原则TDD的最核心思想就是先明确需求,且用代码的方式量化,明确需求标准,然后进行编码实现以达成由代码测试来衡量的标准.那么它要求,先把需要标准写出来,每次只写一个.编码实现通过达到,并刚好满足这个标准.这样一点一点的迭代.这样有三个好处:一个是先明确标准,不至于我们迷失主题,偏离方向.有标准在检测,保证代码是正确的.仅满足当前测试,不至于过早优化和过度设计.2. TDD的难点难点在于如何设计这个测试标准,1)让它足

  • python 基于DDT实现数据驱动测试

    简单介绍 ​ DDT(Date Driver Test),所谓数据驱动测试,简单来说就是由数据的改变从而驱动自动化测试的执行,最终引起测试结果的改变.通过使用数据驱动测试的方法,可以在需要验证多组数据测试场景中,使用外部数据源实现对输入输出与期望值的参数化,避免在测试中使用硬编码的数据,也就是测试数据和用例脚本代码分离. ​ DDT它其实就是一个装饰器,它会根据你传递进来的数据来决定要生成几个测试用例. ​

  • 浅谈测试驱动开发TDD之争

    前言 在历史上有很多精彩绝伦的神仙打架,比如数学界的牛顿和莱布尼茨关于微积分的旷世之争:比如量子物理中的爱因斯坦和波尔的紫禁之巅:比如足球里的梅西和C罗的旗鼓相当难分高下:又比如滴滴和快滴之间触目惊心的烧钱大战--而在软件行业中,也同样有神仙打架的名场面,那就不得不提的是2014年的那场--测试驱动开发(TDD)之争. 比赛的红方是David Heinemeier Hansson,蓝方是Kent Beck.David Heinemeier Hansson 由于名字较长简写成DHH,Ruby on

  • 使用 Django 进行测试驱动开发

    目录 创建项目 配置 app 编写测试用例 编写代码 执行测试 最后的话 所谓测试驱动开发(TDD),就是先编写测试用例,然后编写代码来满足测试用例,具体包含以下步骤: 编写测试用例. 编写代码满足测试用例中的需求. 运行测试用例. 如果通过,说明代码满足了测试用例所定义的需求. 如果未通过,则需要重构代码,直到通过. 重复以上步骤,直到通过全部的测试用例. 通常情况下,我们都是先写代码,然后编写测试用例,因此测试驱动开发是反直觉的,那为什么还要这么做呢?基于以下几点原因: TDD 可以被认为是

  • 浅谈JavaScript前端开发的MVC结构与MVVM结构

    MVC Model–View–Controller (MVC)是一种把信息展现逻辑和用户交互分离的计算机用户界面开发模式:Model包含应用的数据和业务逻辑:Controller负责把用户的输入,转换为命令传递给Model和View;这是维基百科的解释: 这种模式最初是由Trygve Reenskaug在使用Smalltalk-80(1979)工作时设计的,刚开始叫做Model-View-Controller-Editor:后来通过<Design Patterns: Elements of Re

  • 浅谈使用PHP开发微信支付的流程

    下面以PHP语言为例,对微信支付的开发流程进行一下说明. 1.获取订单信息 2.根据订单信息和支付相关的账号生成sign,并且生成支付参数 3.将支付参数信息POST到微信服务器,获取返回信息 4.根据返回信息生成相应的支付代码(微信内部)或是支付二维码(非微信内),完成支付. 下面分步骤的讲一下: 1.微信支付中相关的必须的订单参数有三个,分别是:body(商品名或订单描述),out_trade_no(一般为订单号)和total_fee(订单金额,单位"分",要注意单位问题),在不同

  • 浅谈Spring Boot 开发REST接口最佳实践

    本文介绍了Spring Boot 开发REST接口最佳实践,分享给大家,具体如下: HTTP动词与SQL命令对应 GET 从服务器获取资源,可一个或者多个,对应SQL命令中的SELECT GET /users 获取服务器上的所有的用户信息 GET /users/ID 获取指定ID的用户信息 POST 在服务器上创建一个新资源,对应SQL命令中的CREATE POST /users 创建一个新的用户 PUT 在服务器上更新一个资源,客户端提供改变后的完整资源,对应SQL命令中的UPDATE PUT

  • 浅谈Android App开发中Fragment的创建与生命周期

    Fragment是activity的界面中的一部分或一种行为.你可以把多个Fragment们组合到一个activity中来创建一个多面界面并且你可以在多个activity中重用一个Fragment.你可以把Fragment认为模块化的一段activity,它具有自己的生命周期,接收它自己的事件,并可以在activity运行时被添加或删除. Fragment不能独立存在,它必须嵌入到activity中,而且Fragment的生命周期直接受所在的activity的影响.例如:当activity暂停时

  • 浅谈Android app开发中Fragment的Transaction操作

    在Android中,对Fragment的操作都是通过FragmentTransaction来执行.而从Fragment的结果来看,FragmentTransaction中对Fragment的操作大致可以分为两类: 显示:add() replace() show() attach() 隐藏:remove() hide() detach() 对于每一组方法,虽然最后产生的效果类似,但方法背后带来的副作用以及对Fragment的生命周期的影响都不尽相同. add() vs. replace() 只有在

  • 浅谈使用mpvue开发小程序需要注意和了解的知识点

    一.实例生命周期 除了Vue本身的生命周期处,mpvue还兼容了小程序的生命周期,这部分生命周期的钩子来源于微信小程序的Page,除特殊情况外,不建议使用小程序的生命周期钩子. app 部分: onLaunch,初始化 onShow,当小程序启动,或从后台进入前台显示 onHide,当小程序从前台进入后台 page 部分: onLoad,监听页面加载 onShow,监听页面显示 onReady,监听页面初次渲染完成 onHide,监听页面隐藏 onUnload,监听页面卸载 onPullDown

  • 浅谈Angular7 项目开发总结

    由于公司需要,开始学习angular,这个传闻中学习曲线及其陡峭的前端框架,并开始写第一个用angular的项目,截止今天初步完成现有需求,顾在此做一次遇到问题的总结,以便知识的掌握. 一.在angular项目中,如何使用锚点 在常规项目中,使用锚点用来做"智能"定位效果时,只需这么写: <a href="#test" rel="external nofollow" >走你</a> <div id="tes

  • 浅谈VUE uni-app 开发环境

    目录 1.通过 HBuilderX 可视化界面 2.通过 vue-cli 命令执行 总结 1.通过 HBuilderX 可视化界面 a. 创建uni-app: b. 运行uni-app; c. 发布uni-app 2.通过 vue-cli 命令执行 a. 安装node.js,下载地址:https://nodejs.org/en/download/ ##检查node.js 版本 node -v v14.18.1 npm -v b. 全局安装 vue-cli npm install -g @vue/

随机推荐