如何说服你的同事使用TDD?

如何说服你的同事使用TDD?

如何说服你的同事使用TDD?

  TDD(Test-driven development),也就是我们常说的“测试驱动开发”,是由 Kent Beck 在1996年提出的概念。TDD这个术语,经常被人挂在嘴边,然而真正在项目实施,却寥寥无几。

  是TDD对开发者要求太高?还是TDD根本就不值得去做?

  非也。

为了让大家对TDD有一个具体而亲切的认识,我先给大家举一个在编程中使用TDD进行开发的实际例子。

  

Bob大叔的保龄球训练

  这是一道计算保龄球比赛一局总得分的编程题,保龄球的计分规则非常简单:

  • 每一局总共有十轮,每轮一开始会有十支球瓶,球手可以扔两次球,目标就是用尽量少的球把全部球瓶击倒。

  • 如果第一球就把全部的球瓶都击倒了,也就是STRIKE,画面出现“X”,就算完成一轮了,所得分数是10分再加后面两球的倒瓶数,

  • 如果第一球没有全倒,就要再打一球,如果第二球将剩下的球瓶全都击倒,也就是SPARE,画面出现“/”,也算完成一格,所得分数为10分再加下一格第一球的倒瓶数,

  • 如果第二球也没有把球瓶全部击倒的话,那分数就是第一球加第二球倒的瓶数,没有奖励(bonus),再接着打下一格。依此类推。

  • 第十轮有机会扔三次球。如果在第十轮出现STRIKE或者SPARE,则球手可再加打第三球。

  • 全部十轮的得分相加就等于这一局的总得分。

  •   题目要求我们提供一个名字为Game的类,这个类有两个方法:

  • roll(pins : int):每次球员扔球后执行这个方法,入参是此次扔球击倒的球瓶数量。

  • score():每局比赛结束时执行的方法,返回这局比赛的总得分。

  •   下面开始使用TDD来完成这个编程训练。

      如果此时你已经在开始构思要如何实现,请打住!因为这不是TDD的风格。

      记住,先别想着怎么去实现,先写测试用例,也就是先把你调用这个Game类的代码写下来。

      首先,我们创建一个BowlingGameTest类:

      import junit.framework.TestCase; public class BowlingGameTest extends TestCase { }

      接着添加第一个测试用例:

    如何说服你的同事使用TDD?

      当我们刚刚new了一个Game对象时,编译器就提示错误了,此时暂停测试用例的编写,开始编写产品代码!(这么做似乎有点过于耿直,不过对于加深对TDD的印象还是很有帮助的)

      我们创建了Game类,此时编译通过,执行所有单元测试,绿条!

    如何说服你的同事使用TDD?

      接着我们在第一个单元测试中调用roll方法和score方法,同样的,我们遇到编译不通过的问题,再依次给Game加上对应方法后,我们得到了下面这段代码:

    如何说服你的同事使用TDD?

      我们心里很清楚,这个代码是经不住考验的,我们随便添加一个单元测试,都可以让测试用例不通过。比如我们让一个保龄球世界排名倒数第一的球手去比赛,每轮他都只击倒一个球瓶,测试用例毫不犹豫地失败了:

    如何说服你的同事使用TDD?

      于是我们要修改一下逻辑,在每次roll的时候,加上分数。细心的读者可能还发现了,下面这段代码还对测试代码进行了重构,把每个单元测试都要做的new Game()操作抽取到了setUp方法中:

    如何说服你的同事使用TDD?

      接着我们又发现我们经常要模拟很多次击倒相同数量球瓶的操作,因此我们把这个操作抽取成一个rollMany方法:

    如何说服你的同事使用TDD?

      我们的代码到这里还完成不到1/3,但是我们已经做了两次重构,没错,TDD的过程,也是不断小步重构的过程。

      接下来,我们测试一下Spare的场景,这一次测试用例又理所当然的失败了(不要担心一次次失败会打击自信心,因为这些都是我们刻意制造的失败,人们面对意料之中的失败往往更有激情)。而当我们准备动手修改产品代码时,却发现了一个代码设计层面的问题,那就是我们在roll方法里面做了roll不应该做的事情,roll意味着扔球,而我们却在里面修改了得分:

    如何说服你的同事使用TDD?

      此时我们需要把新增的用例暂时屏蔽掉,然后对产品代码进行重构,roll专心做它的事,把计算得分的活交给score来做:

    如何说服你的同事使用TDD?

      接下来,就是继续放开我们之前屏蔽掉的测试用例,继续修改产品代码,让测试用例通过,然后再添加STRIKE的测试场景、添加更多的测试场景…… 这些过程就不再赘述了,因为作为一个TDD的例子,前面这几个步骤,已经足够让大家对TDD有一个比较深刻地理解了。

      关于这道题目的完整解答过程,大家可以到Bob大叔的TheBowlingGameKata去下载对应的PPT。

      

    TDD的三项法则

      上面的保龄球训练中,我们一直在遵循着TDD的三项法则:

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

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

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

  •   遵循着三项法则,我们开发的过程就是下面这五个步骤不断循环、小步迭进的过程:

  • 添加测试用例。

  • 运行所有测试用例,如果新用例失败了,执行下一步,否则返回上一步。

  • 编写产品代码。

  • 运行所有测试用例,如果通过,执行下一步,否则返回上一步,直到写出满足测试用例的代码。

  • 重构。

  • 返回第一步,继续循环。

  • ......

    (点击阅读原文,查看全文)

    如何说服你的同事使用TDD?


     推荐阅读

    点击阅读?TDD之团队单元测试规范

    点击阅读?TDD在Unity3D游戏项目开发中的实践

    点击阅读?开发人员看测试之TDD和BDD



    点击阅读?测试驱动开发(TDD)已死,我们该何去何从

    点击阅读?TDD中的单元测试写多少才够?

    上文内容不用于商业目的,如涉及知识产权问题,请权利人联系小编,我们将立即处理。

    如何说服你的同事使用TDD?


    如何说服你的同事使用TDD?

    点击“阅读原文”,查看全文内容