基于 Rational Functional Tester 的 GUI
测试
自动化测试技术是保证产品质量的重要手段。随着敏捷开发方法的盛行和产品发布周期的缩短,产品对测试的要求也相应提高,因此自动化测试也变得比以前更加重要。冰冻三尺非一日之寒,和产品开发一样,产品的自动化测试系统也需要通过迭代开发不断强化,逐步解决效率瓶颈,更广泛、更灵活的支持产品开发的需求。那么,如何从无到有建立图形用户界面(GUI)产品的自动化测试系统?GUI
产品自动化测试每一阶段的目标是什么?在逐步演化的过程中,我们可能会面对哪些问题?这些又可以如何解决呢?本系列文章希望能够结合我们团队的经验,一一解答。在本文中,您将了解我们如何看待自动化测试,又是如何通过
RFT 来实现简单的测试目标。
踏上自动化测试之路
在中国,大多数开发者并不会太关心测试,保证产品质量似乎更多是测试者应该关注的问题。所以,当只写过单元测试代码的我告诉面试经理我认为“自动化测试如何重要”的时候,我可以明显感觉到他很开心。
当然,我最终还是去做了开发而非测试。我们的团队把 Lotus Notes
这一至今有 21 年历史的 Lotus 的拳头产品从 Windows 平台成功的移植到了 Mac 平台。如果您熟悉
Lotus Notes,您会明白我们的不易。这个的产品的软件架构非常庞大,8.0 版以上的 Notes
基于 Eclipse 平台,而其部分核心逻辑和界面组件仍是由 C 完成的。(如果您希望进一步了解这个产品,欢迎您到
Lotus 专区转转)
随着项目的顺利进行,测试团队找到了我们:我们是否有方法在 Mac 上进行自动化的测试呢?如果能够在每天的最新版上自动跑一轮冒烟测试,那么我们将不再需要浪费一个测试者每天半小时的时间来痛苦的重复。
这是我们和自动化测试的第一次亲密接触。其后,随着敏捷方法的在项目中的推行和产品发布周期的缩短,我们对自动化测试的要求也越来越高,我们参与了更多系统改进的工作,也接触了更多领域的专家。从一群菜鸟开始,步步走来,我们理解了建立自动化测试系统的不易:和产品开发一样,产品的自动化测试系统也需要通过迭代开发不断强化,逐步解决效率瓶颈,更广泛、更灵活的支持产品开发的需求。往事历历在目,在从本文开始的五篇系列文章中,我们希望和您一起来分享我们的心得和感悟:
在本文中,您将了解我们如何看待自动化测试,又是如何通过 RFT 来实现简单的测试目标。
在第二篇文章中,您将看到我们如何像做产品一样去经营测试,构建易于维护的基础系统。
在第三篇文章中,我们将给您展示我们是如何通过完善的日志功能和使用规则提高分析测试结果的效率的。
在第四篇文章中,您将看到一些高阶使用技巧,包括如何支持自定义控件、如何实现测试脚本的跨平台以及如何通过开发新的工具来自动的创建自动化测试脚本。
在第五篇文章中,您将看到我们如何通过 LA 灵活的使用我们的测试资产,实现自动部署自动化测试环境以及分布式测试的目的。
好了,现在,让我们正式启动我们的探索之旅。如果您也对 GUI 产品的自动化测试感兴趣的话,请加入我们!
了解自动化测试
和产品开发一样,构建自动化测试系统同样需要人力物力的持续投入。所以,在我们开始考虑实施之前,需要对自动化系统有所理解,以帮助我们调整预期并设定合适的目标。那么,自动化测试到底是什么?能够为我们带来哪些好处?实施的切入点在哪里?又应该如何持续改进呢?百度百科的“自动化测试”词条对这些方面做了概要性的介绍,推荐您阅读。在此基础之上,我们还希望您能够理解以下一些内容:
自动化测试如何改善产品质量
首先,通常说来,受限于人力资源、产品规模和发布周期,任何大型产品都很难在发布之前对产品的各个方面进行全面的测试。我们只能根据新版本中的代码变更预测影响的功能和行为,并围绕这些影响区域进行重点测试,而对于其他区域测试完整性将大打折扣。这就为产品缺陷的滋生留下了隐患。另一方面,很多的测试都需要机械性的重复执行,比如冒烟测试、回归测试、性能测试、和长期运转测试(Long
Run),这些测试的手工执行将占用大量的人力。由于缺乏充足测试的支持,开发者对产品结构性问题修正的风险将大大增加,从而使得开发者更倾向于针对问题的表象“打补丁”而不敢触及根本。
通过自动化测试,测试团队可以从这些繁冗的机械重复中解放出来,集中精力加强重点区域的测试力度,也有余力通过制定更完善的测试计划,设计合适的测试用例,改进自动化测试脚本来提高测试的整体覆盖率;从开发者的角度看,随着回归测试成本的降低,开发者也更有信心对产品进行结构性的调整。
自动化测试无法代替测试者
虽然 IBM 的“沃森”能够赢得智力问答比赛,但这并不代表当前的自动化测试具备足够的“智能”来替代测试人员。自动化测试只能够按照指定的步骤来执行测试,它实际上是在帮我们完成需要不断重复或者通过人工执行容易出错的工作,充当我们的“廉价劳动力”。而测试人员则能够解脱出来,进行更多创造性的工作。要知道测试人员在项目中永远是短缺的。
自动化测试的覆盖范围并非越大越好
100% 的自动化测试只是一个美好的愿景。事实上,有很多测试工作目前是计算机难以完成的。比如说,我们需要检查一篇文档打印稿的格式正确性,自动化测试脚本要如何实现呢?如果能够通过计算得出正确的结果,我们完全可以直接把它作为产品里的排版代码了。事实上,人所擅长的模糊判断恰恰是程序的短板。另外,自动化脚本的编写和维护同样需要成本,如果脚本的复用和重复执行所带来的收益小于我们的付出,这种自动化将变得毫无价值,因此,自动化测试也不适用于变化较频繁的功能区域——我们需要在覆盖率和成本之间寻求一个平衡。
自动化测试无法发现新的问题
呃,这是很奇怪的一个观点,对吧?在我们编写和调试自动化脚本的时候,或许能够发现一些产品的新问题,而一旦脚本投入使用之后,将不会帮助我们发现“新”的产品问题。因为,这些脚本只能不断的重复,来确保我们设计的测试用例在今后的日子里不会发现问题。自动测试脚本充当的更像是一个“守护者”而非“探索者”的角色,它将帮助我们更加确信产品没有问题。它们发现的问题通常都是回归性的,而不是新问题。
自动化测试需要成本
这又是一个显而易见的命题,不过自动化测试的成本经常会被忽略。和我们的产品一样,自动化测试脚本也是一堆代码。我们通过自动化测试来保证产品的正确性,而保证这些脚本的正确性却需要人工,这意味着我们需要开发、调试;产品是会不断演化的,昨天正确的脚本今天可能就是错误的,这意味着我们需要不断的维护。为了复用现有的脚本获得更大的价值,我们需要持续的改进自动化系统,甚至移植到新平台上。开发,维护,演进,请记住自动化测试需要长期的持续投资。
看过了上面这些内容,您是否会觉得自动化测试不再像想象中的那么美妙了?呵呵,请相信自动化测试并非万能,而现在的大型产品没有自动化测试的支持是万万不能的。作为我们的工具箱中的一个强力工具,我们需要用好它,发挥出它的威力。
如何进行 GUI 产品的自动化测试
四年前,在我们决定开始支持自动化测试时,所面对的最直接的问题就是,我们应该如何进行
GUI 产品的自动化测试呢?对于普通的单元测试,我们只需要给目标函数一个输入并检测输出就可以了;对于 Server
端逻辑的测试,我们只需要模拟客户端发起一个 HTTP 的请求就可以了;对于浏览器里的 Web 应用,我们理论上也可以通过
DOM 树进行直接操作。那么,对于在本地运行的 GUI 产品呢?我们又应该怎样去支持界面交互操作的自动化?在实际工作中我们发现这个问题其实可以分解成三个基础问题:
如何捕获需要操作的对象?
如何对捕获到的对象进行操作?
如何验证操作的结果?
好吧,让我们举个简单的例子来详细说明这三个问题。
下图 1 展示了一个基于 Java SWT 框架的 GUI 应用程序,它可以帮助您从
CD 库中挑选 CD 并下订单。您可以简单的通过列表选择您喜欢的 CD,然后通过点击“Place Order”按钮来下订单。
图 1. CD 订购程序
在您点击之后,您将会首先看到如图 2 所示的登录界面。
图 2. 登录对话框
程序有了,我们应该如何测试下面这个简单的用例呢?
在 CD 列表中选中 CD“Die schone Mullerin, Op.
25”
点击“Place Order”按钮
验证登录对话框能够正确弹出
回顾我们的问题:
如何捕获需要操作的对象?
首先,我们要发现并定位 treeview 控件(包括特定的条目“Die
schone Mullerin, Op. 25”)以及名为“Place Order”的按钮。在 Windows
下,我们可以感知每个原生应用程序的控件组织关系,比如一个对话框上有哪些按钮(您可以尝试使用微软的 SPY++
去观察每个原生程序的窗体结构)。而在 Mac 上,我们只能借助系统的 Accessibility 特性支持发现程序的部分架构,这将受限于程序对
Accessibility 的支持。对于我们来说,最稳妥的解决方法,还是在被测程序中加入一个插件,来直接向我们“汇报”程序的窗体结构和控件信息。
如何对捕获到的对象进行操作?
在捕获到 treview 和按钮之后,我们需要模拟用户展开 treeview,点击选取“Die
schone Mullerin, Op. 25”条目,再单击“Place Order”按钮。相对而言这一点比较容易实现:我们只需要在系统级别控制住鼠标和键盘输入,并模拟输入对应的事件就可以了。当然,要实现操作的跨平台,我们还需要针对不同的操作系统进行封装。
如何验证操作的结果?
在单击“Place Order”按钮之后,我们还需要找到名为“Member
Logon”的对话框,并且验证对话框中的子控件的属性(如文字、位置等)是否与我们预期的一致。和第一个问题的答案一样,通过系统框架能够访问的对象信息是非常有限的。当然,对于验证而言,我们还可以通过局部截屏来比较操作结果(实际上,在最开始的时候,我们就是这么做的),不过这么做的局限性非常大。首先,用于截屏的基准测试机器和普通的测试机器需要在许多方面保持一致性,比如分辨率、颜色质量等。另外,图像匹配的效率也不高。最重要的是一旦我们调整了产品里控件对象的形状、位置以及文字等属性,以前所截取的包含这个控件对象的样图将全部作废,必须重新截取,这将使得维护成本大增。所以,最稳妥的方法,依然是通过插件从产品中直接获取信息。
好吧,转了一圈我们又回来了。在我们试用了很多方法之后,发现最彻底的解决方式还是通过插件来直接获得对应用程序的控制权。而且,我们对自动化开发工具的需求还不止于此:我们希望能够用熟悉的语言开发脚本,毕竟没有多少人愿意为了写个脚本专门去学
Perl;方便的开发环境和脚本的版本管理自然也是需要考虑的因素;一个灵活而有效的日志框架是分析测试结果和调试测试用例必不可少的工具…
…
这么这么多的需求,分明就已经是一个产品了,开发出来都可以卖钱了。
幸好 Rational Functional Tester 为我们提供了这一基础,并且比我们所需要的提供的更多。假如我们不得不从轮子造起的话,恐怕管理团队就需要重新考虑自动化测试的性价比了。
初识 Rational Functional Tester
Rational Functional Tester(简称 RFT)是
IBM 提供的强大的自动化功能和回归测试工具,至于其具体如何强大本文就不赘述了,您可以参考文末参考资料里给出的
RFT 的产品专题。现身说法,Lotus Notes 的大量的自动化测试脚本都是建立在 RFT 之上,包括功能测试、性能测试和本地化测试。对于我们来说,RFT
是一个值得信赖的工具。RFT 的特性很多,以下仅列出我们所关注的一部分关键特性:
支持多种应用程序,包括 Win32 Native、Eclipse、.Net,
浏览器和其他一些应用
支持使用 Java 和 VB.NET 进行脚本开发
支持操作的录制和回放
和 Eclipse 紧密集成,支持方便的开发和调试
通过插件可以和 ClearCase 紧密集成
易于扩展,可以灵活的适应被测程序的需求
下图展示了 RFT 的使用界面,如果您曾经使用过 Eclipse,您将会对此感到非常亲切。
图 3. RFT 使用界面
在 RFT 的众多特性中,录制和回放是非常值得一提的功能,测试人员可以直接通过录制屏幕操作来生成测试脚本,然后通过回放来执行测试。有经验的测试人员也可以通过它来快速生成有用的代码片段。
下面让我们来看一看如何使用 RFT 录制功能来生成上一章中的测试用例的脚本:
运行我们的 CD 订购程序:C:\Program Files\IBM\SDP\FunctionalTester\FTSamples\
ClassicsJavaA.jar。实际上,这是 RFT 自带的一个示例程序。(RFT 在安装后会对系统
JVM 进行配置,所以我们不再需要对普通的 SWT 程序安装额外插件,直接运行就可以了)
在一个空白的 RFT 测试脚本中,点击 "Script->Insert
Recording" 菜单,RFT 将弹出“Recording”对话框,启动脚本录制。
手工操作我们的 CD 订购程序,选择条目,点击“Place order”按钮,然后点击
Cancel 关闭弹出的“Member Logon”对话框。
执行完毕后,在 Recording 对话框中点击停止按钮,录制就会终止。RFT
会生成测试用例步骤所对应的代码。如图 4 所示。
图 4. RFT 录制生成的测试脚本
您可以通过点击“Script->Run”菜单来运行新生成的脚本,一切都很简单。
如果您足够细心,就能够在图 4 中发现更多的信息。RFT 在生成脚本的同时,会把从应用程序捕获的
UI 信息保存在映射文件(object map)里面,比如 tree2 和 placeOrder。而在生成的脚本中将直接使用对象上的方法,这些方法在底层将会最终映射到具体的鼠标和键盘事件。
图 5 展示了 RFT 本身的录制流程如,对于每个脚本,都有独立的映射文件。RFT
会从映射文件里面查找,如果找不到当前测试对象,会把测试对象的信息加入到映射文件里面。
图 5. 录制流程
如果您更细心的话,您会发现 RFT 的录制完成了捕获和操作的功能,而没有生成验证相关的代码,因为这些需要您随后自己补齐。如果您对如何更高效的做验证感兴趣的话,欢迎关注我们的后续文章。
小结
本系列的第一篇文章到此告一段落了。相信现在您已经了解了在启动项目的自动化测试之前应该注意些什么,以及怎样对一个
GUI 产品进行自动化测试。如果您还没有使用过 RFT 的话,建议您不妨下载一份,亲自动手试试它的录制和回放功能,相信它会带给您惊喜的。
|