面向对象一文搞定,终生难忘(面向对象心得)
967
2022-05-30
第10章
测试驱动开发
我们碰到过很多团队,他们都说自己是敏捷团队,但依然严重依赖于手工测试。开发人员把软件扔过墙头给测试人员找问题;测试人员再把它连同大量缺陷报告踢回去。时间就这样消耗在开发人员和测试人员的你来我往之中,拼命给软件打补丁,好得差不多了再交付。
鼓励团队转而尝试测试驱动开发(Test-Driven Development,TDD)以减轻这种压力。团队可以使用自动化的测试来检查代码是否可以工作,只需等待数分钟,而非数小时甚至数天。如此一来,有了坚实的基础,开发人员就能更自信地开发,测试人员也能专注于边界案例,而不必为无关紧要的问题浪费时间。
进入这个自动化测试的涅槃,是你作为敏捷教练将要面临的最大调整。这是一个很复杂的转变,因为引入TDD需要应对技术、个人发展和团队协作方面的挑战。让我们一起来了解TDD实现之路该如何起步,如何克服障碍。然后我们再来看该如何帮助团队转向持续集成。
10.1 引入测试驱动开发
要允许团队花大量时间完成向TDD的迁移。他们很可能会花上好几个月才能做到真正用测试来驱动他们的编码。要想实现TDD,第一个挑战就是找到切入点。我们推荐一次只选择一个问题,而不是试图搞一刀切的大动作。
如果团队刚刚从零开始一个项目,就可以直接全面施行TDD(如前面补充材料所描述的那样)。然而,大多数团队都是在尚未有自动化测试的现有代码的基础之上进行开发的,他们面临的第一个挑战是如何给遗留代码写自动化测试。不必急于用测试来驱动代码,让团队先从每天写一些自动化测试开始,以缓解团队步入TDD的压力。这样一来,在试图以测试优先方式工作前,他们才有时间可以提升技能并搭建测试基础设施。
花时间和团队一起工作,要理解代码的真实状态、团队成员的能力水平以及每一个人到底有多大兴致想要改变自己的工作方式。这时可以使用PrOpER循环(参见1.4节)来消除采纳TDD所要面对的一切障碍。
下面的故事诠释了可能遭遇的一些典型挑战。
引入TDD的脚步太快
Rachel
若干年前,我有一个合作团队的情况好像很适合尝试TDD。他们当时在用Java开发一个内容管理系统。他们的开发经理已经安排好让团队开发人员去参加培训课,磨炼JUnit编程技能。他让我继续跟进辅导团队使用TDD。这个请求看起来十分清楚明白,但我并没有意识到自己接下来会面对什么状况。
表面上看来,这只是一个技术型的挑战而已,团队在他们的代码中嵌入了对一些第三方文档管理系统的调用。他们需要想方设法做到一点:无须调用这个库也能写测试。在我看来这不难做到。我很有信心他们可以使用测试替身(Test Double) [1]来消除对库的调用。然而,在把TDD引入团队的时候,需要解决的可不只是技术方面的挑战,还有和人相关的挑战。
我开始安排自己和队内每个开发人员都结对编程一次。我的计划是,尝试给团队正在开发的用户故事编写JUnit测试,由此入手。然而,刚开始第一天我就碰到了一大堆难题,昭示着这个团队尚未准备好接受TDD。
那一天,刚开始还挺好,我和技术主管Dom坐一块结对。他看起来挺忙的,不过还是很愿意尝试写一些自动化测试。他刚刚好才修复完一个缺陷,于是我们决定写一个测试来验证他所完成的修复。他敲击命令行运行新写的测试,很惊讶地看着测试运行失败,很显然他尚未完全修复那个缺陷!我们在单元测试中选择的测试数据触发了一个问题,而早些时候他手动测试代码时就没有考虑到。看起来经历这件事之后,他已经确信给每个缺陷修复都写自动化测试是个好主意!
我接着又和Dave结对,他正在写解析XML格式输入文件的代码,相当简单。他已经写好了一些单元测试可以在Eclipse里运行,我们又多增加了几个简单的测试用例。我介绍了一个XML断言库给他,或许能派上用场,除此之外,他看起来并不需要帮助。
随后的一次结对很是艰巨。John才刚刚接触Java,而且他也还没有领会到太多面向对象编程的基本原则。他也不知道如何使用IDE编写和运行测试。他使用了单一主测试方法,每次想要检查代码时都得修改这个方法才行。他在理解现有系统工作方式时显得很吃力,但当我建议他去问问其他队员时,他却退缩了。我们花了将近一个小时的时间,才把他那个长测试方法给拆开,提取出一些JUnit测试,但这活儿真的没啥收获。
末了我又和Chris结对,他是团队里唯一的合同工,使用的是另一个IDE——NetBeans。他看起来挺有经验的,但他担心着给那些直接调用第三方库的代码写单元测试会很困难。我提出来可以使用模拟对象(Mock Object[2]),他告诉我说有一名开发人员刚刚离开团队,她曾在测试里使用过模拟对象。我们打开她写的那些测试,看起来很不错。可等我们想要运行的时候,就不是那么一回事了。她写出测试后代码也一直在变,甚至无法顺利进行编译!自从她离开后,团队根本没人运行过那些测试。早知如此,还不如她当初干脆别写呢!
问题就出在这里。所谓“团队”,工作起来并不是真的团队。他们各自忙于不同区域的代码,对如何写测试的观点也各不相同。没有人运行别人写的测试,甚至每个人用的IDE都不一样。没有人真心认同,更没人想搞清楚采纳TDD对团队有何根本意义。
在开始做TDD之前,他们需要先做一些基本的铺垫工作。团队需要共同合作确立测试策略,把测试组织起来做成团队任何人都可以运行的一个共享测试集。
后来,我发现了开发经理要团队采纳TDD的动机,因为项目中测试人员的工作量已经超负荷了。他们发现很多代码相关的细碎问题,而这些问题本可避免,只要开发人员能够针对自己的代码做一些简单的测试。但这个经理没有告诉团队这个情况。他们需要知道这一点,才能理解为什么要接受TDD培训和辅导,才有理由进行改变。
团队的认同
正如前面故事所讲,只教团队如何写测试并不够。团队还需要能一致承诺写测试和运行测试。他们需要看到令人信服的理由,才会承诺花额外的时间去写自动化测试。确保他们已经理解TDD的好处并已领会驱动改变的幕后缘由。
让团队聚在一起,商定他们可以做出的承诺。找出阻止他们开始尝试TDD的障碍,列个清单。接着再问他们有什么办法可以移除这些障碍。使用同意梯级(参见2.4节)帮助团队决定应该先把精力花在哪些行动上。
该学学怎么写测试
一旦他们被说服决定转向TDD,队员就需要学习如何操作。想要推他们一把的话,就找几个有自动化测试和TDD经验的人去帮助他们。
指点他们,要想有一个良好的开端,可以参加商业培训课程。不过,如果没有培训预算(或是没有他们所使用编程语言的培训课程),团队就需要自学如何写自动化测试。帮他们发起一个定期的编码道场以提高测试编写技能(参见本页的补充内容)。
团队开始抓紧编写自动化测试之后,要做好准备,团队会变慢。提醒他们,在做计划时要预留些时间学习如何编写自动化测试。团队还应该提前告知客户,由于正在经历学习期,团队速率很有可能会下降。
决定从何处入手写测试
团队不可能一下子就完成所有遗留代码测试的改造。他们需要以迭代方式做这件事。帮助他们找到切入点。
把团队聚在一起商定测试策略,以决定如何测试不同区域的代码。在白板上画一张软件架构的草图(如图10.1所示)。和团队一起走查这个架构,找出其中最能够受益于自动化测试的部分。给白板拍照,把讨论内容留存下来,回头等团队想要继续增加测试的时候,再拿出来看。
补充内容中定义描述的单元测试是一个不错的切入点。隔离待开发代码通常都很容易,团队应该能创建出快速运行的单元测试。可是,团队很可能会发现,那些没有自动化测试的代码之间的依赖关系很乱。开发人员必须想办法把他们正在写的代码从中隔离出来,然后才能给它们写单元测试。可以在《修改代码的艺术》[1]一书中找到一些相关的实效技巧介绍[Fea04]。
我们合作过的大多数团队都从如下这条基本准则起步:为新代码和遗留代码的修改部分编写测试。也可以跟团队讨论一下这种方式,看看他们是否愿意承诺照这样做。如果遵循法则会有太大压力,就跟他们约定,每天写一两个测试,这样至少能保证有些进展。明确告知团队,最有用的测试关注代码中可能有问题的路径,而不是那些无关紧要的方法。可别把目标设为给getter和setter写测试,那不是关键。
团队决定从哪块代码入手开始增加自动化测试之后,提醒他们还要商量好怎么组织这些测试。他们需要考虑把测试放哪里,是与代码放在同一个子目录里还是分开来。保持测试命名方式的一致性也很有帮助。最后一点也很重要,要让团队所有人都能够顺利运行整个自动化测试套件。
自动化测试 敏捷开发
版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。