2008年12月28日星期日

功能测试之美

功能测试之乐:

功能测试定义了产品的业务需求,通过它业务人员可以了解系统是否能在各个业务场景下正常工作。功能测试通常使用某种自动化测试框架编写,这样开发者可以从自动化的功能测试中获得快速反馈,为下阶段新功能的开发或软件内部实现的重构提供帮助。另一方面,它大大减少了手动环节可能引入的错误,而将枯燥的回归测试交给机器完成,在加快测试速度的同时,将质量保证人员解放出来,从而使他们可以更多地关注于创造性的探索测试。通过从用户角度进行的功能测试,团队对系统在真实条件下的可用性充满信心,而自动化的功能测试也大大提高了工作效率。这样一来,产品能以更高的质量,更快的速度进入市场。

功能测试之苦:

* 保持验收条件及其技术实现的同步

在任何开展自动化功能测试的敏捷开发团队中通常会存在两套系统、 一套是BA(业务分析师)\QA(质量保证人员)所编写、维护的验收条件,可能以纸卡、Wiki等形式记录下来。 另一套是验收条件的技术实现。以源代码的形式记录,由开发者维护。两套系统的并行发展, 带来了同步的问题。如何快速将验收条件转变为技术实现?当验收条件变化时如何使源代码部分同步变化?可不可以将验收条件与测试关联起来,利用Rspec的思路消除这个并行的系统?

* 无法系统化的划分测试

速度是阻碍频繁运行功能测试的主要原因。进行功能测试的团队常常花费数十分钟甚至数小时来运行完整的功能测试套件,加快测试几乎总以失败而告终,从用户角度进行业务场景的测试决定了功能测试的速度天生就是缓慢的。随着软件功能的日益完善,更多的测试被添加到套件中,庞大的套件也使得测试运行的时间越来越长。无法快速得到反馈使团队没有安全感,同时大大减缓开发的脚步,烦躁的开发者甚至开始逃避运行测试,将不安全的代码集成到产品中。

只运行相关的测试,听起来这似乎是一个解决方案。当开发者修改了登录模块的实现后,为什么他们非得花费1个小时等待其他模块的测试结果呢?如果可以仅仅运行登录模块相关的测试,将其余的测试留给持续集成工具运行,开发的效率将大大提高。但遗憾的是在XUnit的世界中,系统化的划分测试并不是件容易的事情。 不论是利用文件名和目录来区分,还是手工维护测试套件,最终总会变成难以维护的大泥球。

* 阅读测试花费的大量时间

大量测试代码总是难以阅读。随着项目的进行, 各种不同习惯的缩写出现在代码中、 测试代码中出现的大量的方法、设置数据越来越复杂等都给阅读测试带来了极大的麻烦。面对失败的测试,试图修复它的开发者总是需要在复杂的代码中挣扎着找出测试的意图,过滤掉准备数据的过程,抽丝剥茧地找到这个测试所覆盖的业务流程,分析究竟是哪个产品模块出了问题。出现这样问题的根源是没有对测试的目的(业务价值)和技术实现做出合理的抽象。

能否有这样一个视图,过滤掉所有让人分散注意力的方法(私有方法,准备数据、清理数据的方法等)让开发者可以清楚地看到测试的目的和步骤,甚至让测试以一种自然语言的形式展现出来,让非技术人员也可以轻易地阅读修改测试呢?


* 启动功能测试的花费

在任何一个团队开始功能测试的第一步,总是耗时和痛苦的。下载各种依赖的库文件,在脚本中进行配置, 保证功能测试可以在命令行执行, 同时还要对IDE进行配置,让开发者可以从IDE中方便地运行单个测试。

能不能缩短这个过程,减轻开发者的痛苦?

功能测试工具的趋势:

敏捷联盟在2007年10月召开了专题讨论来展望下一代的功能测试工具。在这次讨论中, 功能测试领域的专家们提出了对下一代功能测试工具的展望:

* 需要一个整合的开发环境帮助团队:重构测试元素、自动完成命令、增量式的语法检验(基于测试领域特定语言)、快捷键支持、调试等等
* 需要更具描述性的测试领域语言,如将可执行文件、文字、表格、图片、颜色整合到一个测试用例中
* 需要特定的测试领域语言使测试更具阅读性并容易维护
* 需要具备可以使用多种方式查看/导航测试的能力,来帮助我们了解某个部分与整个领域或者特性之间的联系;将测试按照领域上下文来组织;按照用户定义的关键字进行搜索(跨横切关注点)

Twist

经历过很多的功能测试之苦,我们团队尝试了使用Twist来编写、管理功能测试,并得到了很好的效果。它是ThoughtWorks Studio为软件团队设计开发的下一代协作功能测试平台,提供了一个编写、执行以及维护测试的丰富环境。Twist以Eclipse插件的形式设计并开发,充分利用了Eclipse强大的编辑功能, 支持测试专家们通过DSL来表达测试意图。通过自动完成功能,开发人员可以利用Eclipse中广为人知的CTRL + 1 (帮助完成)来快速地将验收条件转换为相应的技术实现(测试类和方法)。由于Twist中的Driver是以Spring Bean的形式注入到测试中,开发者可以以Twist作为平台,使用任何熟悉的DRIVER(如Selenium、Watir、fraiksen)来编写测试。

那么Twist是怎样帮助团队来减轻测试之苦的呢?

* 保持验收条件以及其技术实现的同步
之前我们将验收条件保存在Mingle中的故事卡里,而现在QA和BA通过Twist来编写验收条件,并标记为in-progress。在实现的时候,开发者通过IDE来自动生成测试。在任何人修改验收条件或者技术实现时,IDE的重构功能将自动完成它们之间的同步。例如:测试专家们在Twist IDE 中可能写出如下的验收条件:

Search and buy book :
# search for book written by "Martin Fowler"
# add book "refactoring" to shopping cart
# check book "refactoring" is in my shopping cart


开发者通过TWIST IDE, 可以生成如下测试代码(以Java为例)

public SearchAndBuyBook {

public void searchForBookWrittenBy(String name) {
}

public void addBookToShoppingCart(String name) {
}

public void checkBookIsInMyShoppingCart(String name) {
}
}


之后开发人员可以选择通过手工编写代码(例如通过使用Selenium、 watir作为Driver )或者录制的方式(Twist会记录鼠标、键盘在WEB页面上的动作并转变为相应的测试脚本)来实现功能测试。 更方便的是, 测试专家们编写的DSL验收条件在保持可读性的同时,可以像代码一样自动完成、对重构更加友好、并且可以方便的运行。

在开发者对相应的源代码进行诸如重命名或者引入参数(Introduce Parameter)等重构操作时,相应的测试DSL也会被改变.


* 对测试进行标签管理
Twist使用标签(Tag)对测试进行管理,开发团队可以使用任意标签来标注测试。例如:已完成还是未完成测试、对应的故事卡号、相应或者相关的模块名、属于回归测试还是冒烟测试。这样团队可以方便地划分测试。通过使用Twist发布的ant target中的tag属性,你可以轻松地分组运行测试。比如在我们的项目中最广泛使用的一个Tag是in-progress,这样,QA、BA可以随时提交他们的验收条件,这些尚未完成的验收条件(没有相应的技术实现)会被自动过滤掉,不会引起测试的失败。


*更有效率地 阅读测试
Twist很好地在产品中抽象了验收条件和技术实现,并将它们巧妙地关联起来。这样开发者可以通过易于阅读的DSL来快速了解上下文,并通过Eclipse的快捷键F3,快速地在验收条件和技术实现中进行切换,从而更有效率地阅读测试。

* 减少启动功能测试的花费
用户可以通过在Eclipse中创建Twist项目,快速展开测试。 Twist项目包含了运行Twist测试所必需的所有的第三方库。同时,Twist发布了相应的ANT target,大大减少了团队用于启动功能测试的时间。


在使用Twist的过程中,我们也发现了一些问题,主要集中在Twist的IDE不够稳定,会有一些UI的异常等方面。我们已经将BUG提交到了Twist团队的论坛里,相信稍后的版本会更加稳定易用。 通过使用Twist,很好地将团队以功能测试为中心整合在一起,团队中的所有角色可以通过一套IDE来编写、实现、运行、维护测试,大大减少了交流成本,提高了开发的速度,体会了开发之乐。

没有评论: