1.5 使用增量方法
与很多团队一样,我们发现在每两周的迭代后期都有未完成的测试任务。有时候用户故事未完成。比如,我们从一个包含5页向导程序的UI开始一个故事(story),其中只有4页完成了。
有一位程序员提议用一个“强线程”(steel thread)来标识复杂故事——一个将功能点从不同终端隔离开来的小的功能点。我们为它编写测试、写出代码,然后将测试自动化然后转移到下一个线程。那样的话,测试自动化即便在GUI层面,也能与开发保持一致。第一个自动化的测试可能会过于简单,但可以逐步进行补充。
【小窍门】
强线程是一种保证自动化在时间里程碑内完成的方法。
这种方法很有效,对于每一个计划的复合功能点,我们首先将线程画在白板上,并确保在转移到下个线程之前,当前线程的自动化测试及手动测试都已经完成了。
1.6 正确度量
如何知道我们是否取得进步了呢?如何衡量成功?我们需要一些度量。我们的构建过程会对测试进行统计,所以很容易跟踪每一个层次的测试数量:JUnit、FitNesse断言和测试页面、Cannoo WebTest断言等。
这些原始数据并不能代表整个趋势。我们,包括业务经理,都希望看到这些数字在每次迭代中都在增长。在每一次迭代中,指定一份染色的日历——如果某天所有的回归测试至少通过了一次,则将其日期标记为绿色,否则标记为红色。业务人员也非常关注这些数据,如果他们在一行里面看到两个标记为红色的日期,他们会向我们询问原因。他们也会记录每一次迭代中JUnit、FitNesse和Canoo WebTest的测试数量。当测试数量减少时,他们也会关注并询问原因。
【真知灼见】
让经理看到自动化测试的好处。你需要不断“推销”自动化的好处。
让测试结果对外可见也是一种宣传的形式,可在整个公司范围内加强公众对自动化回归测试的了解。客户看到测试数量增加,并且发布的功能点越来越多。所以,当我们申请时间来重构产品或者改善构建过程时,我们的客户就能够理解了。
我们需要快速的反馈,所以每一次构建花费的时间是一种重要的度量。如果运行JUnit测试的持续构建花费的时间超过10分钟,就开始备份检入信息,那么就很难找出是哪一个测试导致了失效。当JUnit构建花费的时间超过10分钟时,我们停下来查看是什么原因导致了运行缓慢。我们重构测试、添加硬件、升级操作系统、配置测试使其并发运行等,这些都会使反馈循环更快。对运行更高级测试的构建也是这么做的。如果FitNesse测试要花好几个小时来运行,那么反馈太慢。我们要在测试覆盖和速度之间权衡。幸运的是,虚拟机可以同时运行多个测试套件,减少反馈周期并且非常廉价。
【小窍门】
不要使自动化停滞不前:在需要的时候进行重构、使用新的硬件、重组测试。记住我们的目标(这里指更快速的反馈),并修改自动化测试来达到我们的目标。
1.7 庆祝成功
100个JUnit测试并不是很多,但每一个测试代表了许多断言,并且是一个值得庆祝的里程碑。当达到1000个JUnit测试时,我们觉得这值得全公司聚会庆祝一下,并为全公司的人购买了比萨。当庆祝第2000个JUnit测试时,我们组织了一个有饮料和拼盘的小型聚餐。有人曾经问过我,这些庆祝会不会鼓励一种不好的事情:盲目增加不必要的测试数量。其实,因为每个测试都是刚好在让其通过的那部分代码之前编写的,所以我们没有进行任何无关的单元测试。如果包含测试的代码移除了,那么测试也会移除。
当接近3000个JUint测试时,敏捷教练让我撰写一份说明书,谈谈在单元测试级别中一个健壮的回归测试套件对业务的重要意义。我写了一份关于TDD的高级描述报告,即它是如何帮助我们开发出健壮的、易维护的代码,并在单元测试级别为我们提供了回归测试覆盖的安全网。我试图阐明JUnit的目的:帮助设计代码。也就是说,我们不是胡乱编写单元测试的。敏捷教练把这份报告发给公司的每个人,并在一家本地饭店预订了一个聚会。不仅是为了庆祝我们完成任务,而且让所有的业务利益相关者也能体会其中的意义。
1.8 引入工程冲刺
因为我们已经花时间向业务经理解释了测试的整个过程和实践,所以他们了解了技术债务的观念。每当我们为了顾及截止日期而走捷径的时候,代码就变得很难处理。如果我们没有时间将工具升级到最新版本,或者没有时间尝试能让我们更高效地工作的最新软件,我们的速度就会下降。这就好比如果我们拖欠信用卡债务,利率就会上升,我们所欠的债务就越来越多。如果我们被技术债务缠身,就无法以客户期待的速率来发布大量的软件。
所以,我们每6个月会进行一次特别的迭代,称为“工程冲刺”在这次迭代中不需要发布任何新的功能点(engineering sprint)。我们可以利用这段时间完成以下工作:升级到最新的Java版本、进行大型重构、尝试新的测试工具、重构FitNesse测试来消除冗余。在一次工程冲刺中,我们将源代码版本控制系统从CVS转变为SVN(Subversion)。在另外一次工程冲刺中,我们将构建过程从CruiseControl转变为Hudson,从而为缩短反馈周期提供了更多的选择。每当在更短的时间内发布了更多更好的功能点时,业务人员就可以看到这些投资的回报。
1.9 团队成功
我们的团队在一年的时间内,从没有自动化测试发展到将所有的回归测试进行自动化。我们的自动化金字塔还不完全是理想的形状,但是我们有了好的测试框架和在每个级别(见图1-2)实现测试的驱动程序。尽管这样,我们却没有满足现状,而是继续寻找在各个级别有效地进行自动化回归测试的方法。当我们可以控制功能测试的自动化之后,我们将着手性能测试的自动化。我们有处理不完的挑战!
最好的是,我们有时间对每个用户故事或功能集合进行充分的探索性测试,这样的话,在产品中就很少会有问题浮出水面。同时自动化测试也可以帮助我们进行探索性测试。我们希望创建:使用Ruby的具有高灵活性的脚本,Watir(Ruby中的Web应用测试)测试驱动,以及测试/单元框架。它们接收运行参数,允许我们在几秒或者几分钟内建立复杂的测试数据和情景,而不再需要花费时间搭建繁琐的手动测试平台。通过脚本可以直接对需要进行测试的部分进行测试,这样我们就可以拥有更多的时间来深入探索软件。
【真知灼见】
使用自动化测试来支持创新型手动测试。
我们的目标是使所有回归测试实现自动化,但是这一目标有几点需要说明。要达到一个合理的ROI,自动化测试在设计时必须要考虑到长期可维护性。因此要求程序员拥有好的设计技巧,可以帮助设计各个级别的自动化测试。我们不断对测试进行重构以使维护费用较低。同时,在选择将哪些测试保留在自动化回归测试套件中时,团队要有准确的判断力,需要“正好”(good enough)覆盖。测试太多意味着反馈周期太长和维护费用过高。同时,我们还有少量的遗留代码没有被自动化测试覆盖,当我们进行可能对遗留代码造成影响的大范围的代码重构时,我们要留一些时间进行手动回归测试。
图1-2 自动测试金字塔使用的工具
我们成功的关键是采用“完整团队”(whole-team)的方法,团队里的每个人都致力于将所有回归测试实现自动化,我们有很多的技术和观点来帮助解决自动化中遇到的问题。自动化测试金字塔的每一层都包含不同的工具,而且大家可以关注不同层。开发人员像写代码那样编写单元测试用例,测试人员更多的是进行GUI测试的自动化,在FitNesse中,双方在中间层合作。然而,在每一个用户故事完成之前,团队中的每个人都负责每个用户故事的所有测试活动。
自动化测试给我们一个快速的反馈周期。如果在几小时内任何一个现有的功能点遭到破坏,在工程冲刺时,我们应该腾出时间来做出改变,使加入更多测试用例后的反馈周期仍然很快。我们知道是代码的哪些改变引起的这个问题,并立刻进行修复。我们可以在经常发布新业务价值的同时,达到公司的要求,开发出最高质量的产品。
1.10 持续改进
2011年是我们自动化测试之旅的第8年,总是要面临新的挑战。正如本章所述,我们的GUI测试套件已经增长到需要2个多小时来运行。这个时间太长了,所以我们将它划分为两个测试套件,并在两个从属机器上并行运行。这需要进行大量的工作,因为有些测试依赖于其他测试,过去没有好好实施而是采取了折中的方法,现在要为此付出代价。我们有超过5400(这个数字还在增长)个JUnit,并且重构的FitNesse测试套件在30分钟内完成。
我们知道在单元级别中的测试覆盖率,但是并不知道在功能性或GUI级别的测试覆盖率。我们目前采用一个的工具进行实验,可以测量出FitNesse测试的代码覆盖率。
相比于8年前,我们现在对自动化测试设计有了更深入的了解,并且我们的测试需要大规模的重构。无论何时,当加入一个测试用例或者对其进行修改的时候,我们都尝试去改善测试,但是只在工程冲刺阶段才进行大规模重构。我们通过一些开源工具进行观测,并不断尝试我们认为可以降低测试维护费用或者提供更快反馈的新工具。
在GUI测试中我们也是这么做的。几年前,当我们发现Canoo WebTest并不能很好地支持JavaScript的时候,就在所有回归测试中开始使用Watir编写新的功能点。约一年之后,Canoo WebTest升级到比那时的Watir可以更好地处理JavaScript和Ajax,而Watir更难与构建过程整合,所以在保留现有的Watir回归测试脚本的基础上,我们又切换回WebTest。
我们也进行了调查,准备使用Slim来替代Fit进行我们的FitNesse测试。同样,我们可能不会立刻把所有FitNesse测试转为Slim,但我们同时发现维护多种工具也没有很大问题。
这些年来,我们的员工相当稳定。一位新程序员和一位测试人员来了又走,但是团队的核心成员一直保留着。我认为这是因为优秀的人才乐意留在他们可以尽最大努力工作的地方,那就是我们团队。比如,我们允许他们做类似自动化回归测试的事情。我们有一些有趣的转变,一位测试人员决定成为一位程序员,所以他参加了一些Java课程的培训,另一位程序员跟他一起合作,他的学习速度非常快。同时,他仍然保留了自己在测试方面的兴趣,尤其在性能测试方面。另一位程序员一直都无法采用TDD方法,尽管经过了大量的指导和培训,因为他的代码始终没有达到标准,最终只好让他离开。我们从不会去刻意填补空缺职位,但我们的团队效率却在提高。每一个团队都需要恰当的人选,而且那些人需要时间来学习、实验和提高。
1.11 总结
你的团队如何?阻碍你进行自动化测试尝试的最大问题是什么?你的团队是否缺乏一个特殊的技能组合?你是否只是需要时间来制定和实施策略?你是否还在等待合适的硬件或者软件?想想你现在能做一些什么事情来提高自动化测试并缩短反馈周期。要有耐心,一步步慢慢来。对你得到的结果进行试验和分析评估,不断进行哪怕只是很小的改进。不知不觉间,你就会享受自动化测试的好处。
(未完待续...)
相关链接:
自动化测试最佳实践 连载一
自动化测试最佳实践 连载二
自动化测试最佳实践 连载三