【IT168 评论】自动化黑盒测试、GUI级回归测试工具在行业中被广泛应用。根据现在流行的说法,即使是只有很少的编程经验的人都能使用这些工具快速创建各种测试套件。按照这种说法,这些工具易于使用,并且连测试套件的维护也不成问题。因此,一位开发部经理很自然地用这些工具之一去替代一些(或很多)麻烦的测试人员,这样既省心又省钱,还能快速发布软件。
以上说法被工具提供商和不懂测试的经理到处散布,甚至还包括那些应该对测试有更多了解的测试人员和测试经理们。
事实上,一些公司成功地使用了这些工具,而另一些公司则没能有效地利用它们。
十三位经验丰富的软件测试工程师讨论了可维护性黑盒回归测试套件的开发过程中的成功及失败模式。讨论的焦点是实际经验。首先,我们承认很多实验室已开发出解决自动化问题的实用方法。因此,我们的目的就是把这些实际经验汇集到一起,在相对较短的时间内获得较大进展。
大家只为同一个目标组织到一起。但成员们有他们自己的观点,并不一定代表整个团体的观点。
本文综合了本次讨论的一些重点议题以及我的一些测试经验。
问题
自动化回归测试有很多缺陷,我在这里只列举了一些。James Bach(LAWST成员之一)在他的文章“Test Automation Snake Oil”中列举了其它的很多种。
基本规范的问题
以下是基于GUI的自动化回归测试的基本规范:
a. 设计一个测试用例,然后运行。
b. 如果程序不能通过测试,就撰写一份错误报告。在bug被清除后再重新开始。
c. 如果程序通过了测试,就自动运行。然后重新运行测试(通过脚本或者借助截取工具)。在测试末尾对屏幕输出进行截图。然后保存测试用例及输出结果。
d. 接下来,运行测试用例并把它的输出与已保存的输出作比较。如果匹配,则表明程序通过了测试。
第一个问题:这种做法开销很大。通常,对自动化测试进行创建、验证、简化文档所花费的时间是创建并手工运行测试的3到10倍,甚至更多。虽然很多测试值得被自动化、但对于那些只运行一两次的测试,这样做并不划算。
有人建议测试人员对他们所有的测试用例都进行自动化。我很不赞成这种观点。因为很多黑盒测试被创建并运行仅仅一次,那么,要自动化这些一次性的测试,就会不得不在每一个测试上花费大量的时间和金钱。而同时,将不能运行大量测试。所以,为什么非要让每一个测试变得低覆盖率、高成本呢?
第二个问题:这种做法带来了额外开销的风险。我们都知道找到并修复bug的成本随时间而升高。当一个产品离它的计划发布时间更近时,就会有更多的人参与工作,比如,内部?测试用户以及制作用户手册和营销材料的人。因此,越晚找到并修复重大的bug,这些人被浪费的时间就越多。如果把先前大部分的测试时间都用在写测试脚本上,那么你将更晚发现bug,到时候付出的代价也会更大。
第三个问题:这些测试并不强大。因为只有那些通过测试的程序才能进行自动化测试,而这样又会发现许多新的bug。我所知的估计值在6%至30%之间。而如果你在创建测试用例时就计算你所找出的bug,这个数值会变得更大。
第四个问题:实际上,许多测试团队只能对那些易于运行的测试进行自动化。测试初期,它们易于设计,而且程序也不能运行更复杂的测试用例。但到后来,这些测试变得失效,甚至还不如熟练的手工测试人员所设计的已经日益粗糙的测试有用。
对可维护性的思考
可维护性需求并没有因为友好的自动化工具提供商忘了提及它们而消失。以下是二月的LAWST会议上我们再次讨论的两个议题:
o 当程序用户界面改变时,为了正确反映并测试你的程序,你需要做多少更新测试脚本的工作?
o 当用户界面语言改变时(比如从英文变成法文),为了正确反映并测试你的程序而修正脚本又变得有多困难?
我们需要处理这些议题的可靠策略。下面是两种无法解决问题的策略:
用截取工具创建测试用例:创建测试用例最普通的方法就是使用自动化测试工具的截取功能。但这种方法很不合逻辑。
在程序的第一段,你很可能不会像下面这样编写代码:
SET A = 2
SET B = 3
PRINT A+B
在代码中嵌入常量显然是愚蠢的。然而,这就是我们用截取工具得到的结果。我们通过截取一段敲键、移动鼠标或者命令行序列来创建一个测试脚本。这本身就是常量,就好象2和3。于是,程序用户界面的最微小的改变也会导致脚本无效。因此,与截取测试用例有关的可维护性开销是不能被接受的。
截取工具通过展示测试工具如何翻译一个手工测试用例来帮助你编写测试代码。它们并非没有用。但是,如果你试图用它们做太多的事情,那会变得很危险。
基于特定基础编写测试用例:测试团队通常在他们的闲暇时间尝试创建自动化测试用例。其总体规划看起来就像“创建尽可能多的测试”。而这并没有统一规划或主题。每个测试用例都是独立设计并编写的,且脚本通常重复同样的命令行序列。这种方法与截取/重放方法一样脆弱。
成功的策略
我们不必为使用这些工具所带来的风险而沮丧。我们中的一些人已comp.software.testing和其他的出版物上为此做了大量工作。这次我们之所以召开会议,就是因为我们认识到一些实验室已经在处理这些问题上取得了重大的进展,只是信息没有共享罢了。显然,对于一个实验室,重要的是其较其它实验室更先进的思想。因此,此时此刻,我们就在一种能使各自的观点被易于挑战和阐明的环境下,评估我们共同的认识。
以下是若干建议,它们有助于开发自动化回归测试策略:
1. 对从自动化中获益的时机重新设定管理期望值。
2. 承认测试自动化开发是软件开发的一部分。
3. 使用数据驱动的架构
4. 使用基于框架的架构
5. 按真实情况安排岗位
6. 考虑使用其他类型的自动化
接下来我们对它们一一分析。
1. 对从自动化中获益的时机重新设定管理期望值
当GUI级回归自动化测试在软件的发行版N中被开发后,我们都认为在发行版N+1的测试和开发中会有很多收益。我想我们会惊奇地发现所有人都赞成该结论,因为我们总是听说(并没有实际经历)对自动化测试的投入会有快速的回报。
以下例子体现了发行版N中的一些收益:
o 对一套用于测试目的的测试(也叫“烟雾测试”)进行自动化可以获得很大的收益。在开发第N版时,你或许会运行它们50次或100次。即使开发每一个测试是手动执行测试时间的10倍,且在可维护性方面的成本也会是10倍,但仍然对每个测试用例节省了相当于30至80倍的手动执行时间。
o 你能通过自动化配置和兼容性测试节省时间,减少人为错误,并获得对已完成工作的良好跟踪。在这样的情况下,你会对很多设备或在很多环境下运行相同的测试。如果你要测试兼容30个打印机的程序,那么你可以在不到一周的时间里收回自动化该测试的成本。
o 回归自动化有助于性能基准跨越不同的操作系统以及同一程序的不同开发版本。
虽然可以利用自动化近期回报的优点,但是在进行目的是短期收效的自动化时,需要注意对每个额外测试用例或测试用例组进行成本验证。
如果要寻找更长期的收效,跨越软件的不同发行版,那么你就应该为第N版认真地考虑设置如下目标:
在一些特殊的领域(如烟雾测试和兼容性测试),为版本N提供有效的回归测试。
为N+1版中范围更广、更有效的自动化测试做一些支持工作。
2. 承认测试自动化开发是软件开发的一部分
不能在缺乏明晰且实际的计划的情况下,开发在下个发行版中继续有效并适用的测试套件。
不能在缺乏明晰且实际的计划的情况下,开发可扩展的测试套件(所产生的代码量可能比正在测试的应用程序更多)。
不能在缺乏明晰且实际的计划的情况下,开发很多没有足够可维护性开销来证明在项目生命周期中的存在性的测试套件。
软件自动化测试与软件开发人员进行的其它所有自动化工作类似。但有一个例外,那就是测试人员要编写自动化代码。
o 就算程序语言很怪异,它也是一种编码。
o 在专门用于测试的某个应用程序中,每个测试用例都是一种特性。
o 从自动化测试应用程序的观点来看,应用程序(你正在测试的那个)的每个方面都是数据。
在研究众多其它软件开发项目后,我们认为软件开发人员(在这种情况下,也就是测试人员)必须做到:
o 理解需求;
o 采用一个能使我们能有效开发、集成以及维护特性和数据的架构;
o 采用并遵守标准。(我不是指如ISO 9000 或者CMM这样的大型标准。我的意思是务必使在同一个项目中的两个程序员使用相同的命名规则、相同的模块文档结构、相同的错误处理方法等。在任何一个程序员团队中,遵守相同规则本身就是一种标准)
o 遵守纪律。
在所有人中,测试人员必须认识到,遵循一个规范方法而不是为了图快而不保证质量地去设计与实现,这对于软件开发很重要。否则,我们就只能面临失败,与我们已测试的众多应用程序的失败结局一样。
3. 使用数据驱动的架构
在对成功项目的讨论中,我们可以看到两类方法:数据驱动设计和基于框架设计。它们既可以被单独遵循,也可以被综合起来使用。
下面是一个数据驱动设计的例子:假设要测试一个让用户创建并打印表格的程序。以下是你能操控的事情:
o 表格的标题。可以改变字体、大小和风格(斜体、粗体、小写还是常规)。
o 标题的位置(表格的上边、下边还是两侧)和方向(文字水平或垂直排列)。
o 标题图形的位置(标题的上边、下边还是两侧),图形大小(大号、中号、小号)。是位图格式(PCX, BMP, TIFF)还是向量格式(CGM, WMF)。
o 表格边框线条的粗度
o 表格行、列的数量和大小
o 每个单元格中字符的字号、大小和风格。每个单元格中图形的大小、位置和旋转角度。
o 打印表格的纸张型号和输出方向

图1 一张表格的特征
这些参数是相互关联的,因为它们同时在同一页起作用。如果行太大,就没有放置图形的空间。如果有太多的字号,程序则可能会用光内存。本例非常需要把各个变量组合到一起来测试,可是这样就会产生大量组合方式。
假设编写100段脚本来测试仅仅100种这样的组合方式。如果某界面的一个元素改变,例如,标题字体从一个对话框转移到另外一个,那么你就可能不得不重新编写每一个脚本。

图2 某表格格式的测试矩阵的前几行
现在,假设根据一个测试矩阵来工作。我们用很多参数值的一种组合来确定一个测试用例。在矩阵中,每一行确定了一个测试用例,而每一列则代表一个参数。例如,第1列可以是标题位置,第2列可以是标题字体 ,而第3列则是标题风格。这样就会得到若干列。
接着,用一个电子表格制作软件来创建你的矩阵,比如Excel.
为了执行这些测试用例,我们编写一个读这个电子表格的脚本,一次读一行(测试用例),并执行这些小脚本来设置每个电子表格中指定的参数。假设我们正在处理图2矩阵中的第2行。第一段小脚本会读取第一列(标题位置)的值,并导航到合适的对话框和入口域,然后根据矩阵中的值把标题位置设置为右。一旦设置好所有的参数,你就能进行剩下的测试。这时,你可以打印出表格并对输出做出评估。
测试程序将为每一行执行同样的小脚本。
换句话说,工作的流程如下:
* 载入测试用例矩阵
*读每一行I(测试用例)
*读每一列J(参数)
*为该参数执行脚本:
· 导航至合适的对话框或菜单。
· 把变量设置为测试项(I,J)指定的值
*运行测试I并评估测试结果
如果改变程序的设计,把标题位置移动到一个不同的对话框,你只需在那个处理标题位置的小脚本中改变几行代码。并且,你只需改变这几行一次。这个改变将对电子表格中的每个测试用例都起作用。与修改每个测试用例的脚本相比,把代码从数据中剥离会非常有效率。
还有其它几种建立数据驱动的方法。例如,Bret Pettichord(LAWST的成员之一)在他的电子表格中添加了命令行。每一行都列出执行一个测试所需的命令序列(一个单元格对应一个命令)。如果用户界面的一个命令序列发生了变化,那么测试人员可以通过修改电子表格,而不是重写代码来更正相关的测试用例。另外,还有其他测试人员使用简单测试用例序列或状态机序列来建立数据驱动。
另一种数据驱动的方法是使用预先创建的文档。假设通过输入一千份文档来测试一个文字处理器。对每一份文档,脚本都要使文字处理器载入文档并执行一系列简单动作(例如打印)。
一个设计良好的数据驱动方法能够使非编程测试规划人员更容易地说明他们的测试用例。因为,他们能把它们简洁地写到测试矩阵中。如果你做得好的话,这种方法会产生另一个作用,即通过自动化工具用一系列表格简明地显示出正在运行的测试用例。
4. 使用基于框架的架构
虽然框架通常与一个或一个以上的数据驱动测试策略一同使用,但它本身却提供了一种完全不同的方法。Tom Arnold(LAWST成员之一)在它的书和课程中论述了这种方法。
框架通过提供共享函数库中的一套函数,把应用程序从测试脚本中分离。这样,测试脚本编写者在处理这里函数时,就可以把它们视为测试工具的编程语言的基本命令。因此,他们能单独编写软件的用户界面脚本。
例如,一个框架编写者创建了函数openfile(p)。该函数用来打开文件p。它可以通过下拉文件菜单、选择打开命令、复制文件名到文件名域、点击OK按钮关闭对话框来完成操作。此外,这个函数也可以变得更复杂一些,比如,添加可扩展的错误处理功能。它能检查文件p是否已经被打开,或者记录打开文件的尝试以及对结果做日志。该函数还可以用一个命令快捷键,而不是菜单导航来弹出文件打开对话框。如果你的测试程序涉及到一个应用编程接口 (API)或宏语言,那么该函数或许能调用一个单独的命令,并把文件名和路径作为参数传递给它。函数的定义经常在变化。但只要openfile(x)能打开文件x,脚本编写者就不必在意。
很多函数库中的函数在一些应用程序中依然有用(或者当它们被设计成可移植的时,也会很有用)。不要指望100%的可移植性。例如,openfile()的某个版本可以在每个应用程序中使用标准文件打开对话框,但你可能需要为那些有定制对话框的程序设计额外的版本。
框架包括若干类函数,从简单应用程序或工具功能的打包到处理完整任务的复杂脚本。以下是一些基本的类型:
a. 定义应用程序的每种特性
你可以通过编写函数来选择菜单选项,弹出对话框,设定变量值或发布命令。如果用户界面改变了其中的一种工作方式,那么你也就改变了函数的工作方式。而当你重新编译或重新链接时,任何一个用该函数编写的脚本都能自动修改。
在处理定制控件时,框架是最重要的,比如自绘制控件。一个自绘制控件利用程序员提供的图表命令来绘制一个对话框。自动化测试工具只能辨认窗口的存在,却不能辨认里面的东西。因此,当工具并不知道按钮是否在对话框中时,如何让它点击这个按钮呢?而当工具并不知道列表框是否在那里时,又如何让它选择其中的表项呢?或许,你可以用某种小技巧来选择列表中的第三项吧,但是你如何选择一个出现在可变长度的列表中任何位置的表项呢?还有一个问题:当改变视频协议后,你如何像往常那样处理这些不可见的按钮和列表框,以及其他的用户界面元素呢?
在LAWST会议上,我们谈到用基于组装件的组装件来处理这类问题。有一些与会者估计,他们把自动化开发的一半时间都用来处理由定制控件所带来的问题了。
这些组装件对于脚本编写者来说,是一系列复杂的、难以维护且越来越严重的干扰。之所以称它们是干扰,是因为它们不是由正在测试的程序本身引起的,而是由工具引起的。因此,它们使测试人员把精力放在工具的缺点上,而不是放在找出并报告程序本身的缺点上。
如果你必须使用自绘制控件,那么封装应用程序的每种特性很可能是你构建框架的最紧迫且繁重的任务。这可以把每个组装件隐藏到一个函数中。如果要使用一个特性,程序员就调用该特性,而不必考虑组装件的存在。而如果用户界面发生改变,则可以在不影响单个脚本的情况下重建组装件。
b. 定义工具的程序语言的命令或特性
自动化工具都有一个脚本语言。你会发现,通过包裹每个命令来增加一个间接层的做法非常便捷。所谓包裹,就是一个被创建在另一个函数周围的例程。这非常的简单,很可能仅仅只需调用打包函数即可。而且,你可以通过修改包裹来增加或替换函数,以避免测试工具中产生bug,或利用更新的脚本语言。
Tom Arnold给出了wMenuSelect的例子,它是一个选择菜单的可视化测试函数。他编写了一个打包函数:可简单调用wMenuSelect的 SelMenu()。这种做法具有灵活性。举个例子,你可以修改SelMenu(),比如,添加日志功能、添加错误处理机制、添加调用内存监视器命令,或其他任何你想添加的东西。当这样做时,每个脚本都能得到这个新的功能,而并不需要额外编写代码。这种做法在强力测试、测试执行分析、bug分析报告与调试目标方面都非常有用。
用过这种方法的LAWST成员说,该方法已经反复收到成效。
c. 定义经常使用的概念统一的小任务
openfile()函数是这种类型函数的一个典型例子。脚本编写者会编写上百个需要打开文件的脚本,但他只会有意识地关注少数脚本中的文件是怎样被打开的。至于其它的,他只是想要文件被迅速可靠地打开,这样,他就能继续测试他真正要测试的东西。添加一个库函数来做这些事情将会节省脚本编写者的时间,并且能改进脚本的可维护性。
这属于直接的代码重用。与其它任何软件开发一样,在测试自动化中也有同样的需求。
d. 定义用在若干测试用例中的更大更复杂的测试用例块
存在着封装更大规模的命令序列的需求。然而,这样做也存在着风险,尤其是当你过多使用时。因为一个很复杂的命令序列很可能在很多测试脚本中无法重用,所以,就不值得花力气去生成它,为它写文档,并为它加入错误检查代码以期成为编写完善的库函数。此外,序列越复杂,当用户界面改变时就越需要维护。那样一来,你的库维护成本就被一组几乎不用的复杂命令决定了。
e. 定义实用程序函数
例如,你可以创建一个以标准方法把测试结果保存到硬盘的日志函数。你也可以创建一个编码标准,即每个测试用例都以对该函数的调用作为结束。
每个工具都提供了它自己的一套预构建实用程序函数。这样,你可能需要也可能不需要更多额外的函数了。
使用框架的风险
你不能同时把所有这些命令都构建到你的函数库中。因为你没有足够多的人力。一些自动化项目之所以失败,正是因为测试人员试图创建一种终极的、无所不包的编程库。而在框架完成且可用之前,管理支持(一些人员的工作)就已经不存在了。因此,你必须权衡先后,随着时间逐渐地构建你的函数库。
不要认为每个人使用函数库仅仅是因为它已经存在。有的人的编码风格与其他人就不同。如果你没有一个编程标准来控制变量命名、函数接口的参数顺序、全局变量的使用等等,那么在一个人看来合理的东西在另一个看来将变的不可接受。此外,有的人不喜欢使用不是他们编写的代码。而另一些人由于参与项目较晚而不知道库中的内容,于是就仓促的在没有了解库的情况下开始编程。因此,你必须管理好库的使用。
最后,要慎重设置期望值,尤其在当你的程序员编写了他们自己定制的控件时。在发行版1.0(或你开始自动化测试的第一个发行版)中,你很可能会花费很多时间来创建一个封装了所有工作区的框架,以至你不得不编写点击按钮、选择表项、选择tab键的代码。这些工作的回报将在你最后花时间编写的发行版 2.0中体现出来。创建框架的开销很大,因此,要设置符合实际的期望值或修正你的摘要。
5. 按真实情况安排岗位
你必须让管理者注意下面几个问题。
首先,很多测试人员都是相对初级的程序员。他们缺乏设计系统的经验,而设计不良的框架则会扼杀项目。但是,太有能力的程序员也会使项目失败。要使自动化成功,你应该在你的测试组中多招募一些高级程序员。
其次,很多优秀的黑盒测试人员并没有编程经验。他们能提供紧要问题的意见,以及其它大多数程序员无法提供的与客户沟通方面的经验。他们是完成大型测试工作所不可缺少的。但是,你不能指望这些人编写自动化代码。因此,你需要一个不要求每个人都编写测试代码的岗位安排和开发策略。你还需要避免产生一种带有歧视性的想法,认为测试编程人员要高于测试非编程人员。这是一种在使用自动化工具的测试组中普遍存在的偏见,但在我看来,这是不理智的和降低生产率的。它会排挤你的高级非编程测试人员,并且会使你把大量精力投入在与实际客户需求无关的程序测试中。
数据驱动方法使非程序员能很好的工作,因为它使他们能通过简单地把测试规划放入电子表格中来开发测试用例。
第三,在把自动化外包给承包者时要特别谨慎。最好是在内部开发,把承包者视为培训者或让他们处理更多的常规事务。
最后,你必须让你的管理者知道,自动化是要花费时间的,而且在做初始自动化编程工作的发行版中你也无法赶回这段时间。如果你要达到你通常的测试级别,你必须增加人力。例如,如果某项目通常需要十个测试人员手工测试一年,而你想要为自动化工作投入两个人年,那你就需要保持这十个测试人员,并增加两个程序员。在下一个发行版中,你才会缩短测试时间。在这一版中,你会在某些任务上节省时间(比如配置测试),而在额外的培训和行政管理上付出更多时间。到项目结束时,你可能已经改进了面对今后困难时快速回归测试的能力,但是在进度的最后时刻,它很可能反而使你缺乏足够的测试,而并非让你有机会削减人力开支。
6. 考虑使用其他类型的自动化
LAWST会议的重点在GUI级回归测试工具,所以本文的重点也是这个内容。在LAWST会议召开前夕,我们每人都介绍了我们在测试自动化方面的经验。其中的一些人报告了大量的成功案例,但大多数案例都涉及到那些编写测试应用程序的程序员的协作。这些成功案例中用到的工具各式各样,说明不同的测试工具能带来不同的收益。
对于基于GUI的回归自动化测试,有很多带有欺骗性的、不切实际的想法。它们制造出测试能覆盖所有地方的假象,并且引起严重的员工跳槽,并且还使你最有技术的员工专注于设计和维护相对较少bug的测试用例。
这些工具确实很有用,但是它们需要大量投资、审慎规划、培训员工以及十分小心地使用。
附录 LAWST会议上得到的若干结论
在讨论会议每天日程的后三分之一,我们把在讨论中得到的一些论述抄写到写字版上并进行投票。我们并非想获得一致意见。这样做的目的,是想看看每条论述与这些经验丰富的测试工程师的经验符合的程度。在有些情况下,我们中的一些人并没有投票,可能是因为我们缺乏对要投票的论述的具体经验,也可能是因为我们认为该论述不够合理。(这里我已经省略了大多数论述。)
如果你正试图把自动化的成本和风险告诉给你的一位经理,那么以下这些投票结果或许是你们讨论的有用数据。
一般原则
1. 这些论述并非终极真理。在规划自动化时,由于投入了这么多的努力,你必须时刻记住哪个问题是你试图要解决的,以及在何种环境下解决它。(一致同意)
2. GUI级自动化测试是软件开发中的一项重大工作。它需要架构、标准和纪律。适用于软件设计和实现的一般原则同样适用于自动化设计和实现。(一致同意)
3. 为了效率和可维护性,我们需要首先开发一个不随特性改变的自动化结构。我们应该开发像特性一样稳定的基于GUI的自动化。(一致同意)
4. 我们中的一些人认为一个公司的自动化工作可以采用演化模式来进行。
第一代(7票赞成,1票反对):在缺乏以前的自动化经验时,大多数自动化工作将经历以下三个过程:
a. 截取/回放失败。我们截取的是比特还是窗口部件都无关紧要(面向对象的截取/重放)
b. 使用个人编程的测试用例失败。(个人编码的测试用例是按他自己的标准编写的,没有遵循一般标准以及构建共享库)
c. 开发维护在正在进行的基础之上的函数库。这个库可以包括脚本测试用例或数据驱动测试。
第二代(10票赞成,1票反对):一般自动化的主动失败源于以下几点:
a. 把截取/回放作为创建测试用例的原则性方法。
b. 使用个人编写脚本测试用例 (个人编码的测试用例是按他自己的标准编写的,没有遵循一般标准以及构建共享库)
c. 使用设计不好的框架。这是一个普遍问题。
5. 测试用例的直接重放会带来低缺陷(一致同意)
一旦一个程序通过了测试,那么以后就不太可能不通过该测试了。这种说法导出了一些论述(没有一个被明确赞成),即自动化测试有危险,因为它给我们一种很舒服的错觉,认为程序没有问题。即使程序今天没有出现昨天出现的问题,但是依然还有很多情况可以使程序出问题。而如果你总是在没有bug的地方寻找,你就永远找不到它们。
6. 在一次自动化测试工作中找到的bug里,60%到80%是在开发测试中被发现的。也就是说,除非你从一开始就用自动化工具创建并运行新的测试用例,不然大多数bug都会在手动测试中被发现。(一致同意)
我们大多数人都不是第一次使用自动化工具来运行测试用例。在传统规范中,你首先要手动运行测试用例,然后在程序通过测试后把它添加到自动化套件中。但是,如果你有一种办法不依靠先前的截取的输出结果就能确定程序是否通过了测试,那么你就能更加有效的使用工具。例如:
要在不同的操作系统版本或配置上运行相同系列的测试。你可能从未在一种特殊配置环境下测试过程序,但是你知道它应该怎样工作。
要运行一个功能等价测试。这时,你可以并行地运行两个程序,并且给它们同样的输入。如果程序的结果总是与对照程序一样,就说明它通过了测试。
要在测试中操作代码,这样就能在出现异常情况时生成日志,比如,程序到达了一个异常状态、做了一个异常状态转换、以异常方式管理内存、堆栈或其他资源、或者做了任何一种错误的事情。先通过大量的状态转换以及记录执行的命令使用测试工具随机地驱动程序。然后在第二天,测试人员和程序员通过跟踪日志来寻找bug和触发它们的环境。这是模拟出的一个简单例子。如果你正在与应用程序团队合作,那你就能像这样,相对自己手工编写新的测试用例脚本,更加扩展地、有效地(每周都可以找到新的bug) 用你的工具创建测试。
7. 当我们与程序员合作开发链接、接口和调试输出时,自动化会更加成功。(一致同意)
很多这种协作方法并不依赖基于GUI的自动化工具,或者说,人们只是用这些工具做测试驱动,并没有考虑我提到的基于GUI的回归测试规范。在 LAWST会议的第一天,我们兴高采烈地听着自动化的成功案例。但这其中,很多重大的成功都涉及到与编程团队的协作,而并没有涉及到传统的基于GUI回归测试工具的使用。
我们将在今后的LAWST会议中探讨可协作的测试设计与开发。
8. 大多数用截取工具生成的代码是不可维护的,且没有长期价值。但是,在编写测试时,截取工具很有用,因为它能显示工具是如何翻译一系列最近发生事件的。而且,用截取工具生成的脚本能在你编写代码时给你有用的信息。(一致同意)
9. 我们根本不使用屏幕截图,因为这是在浪费时间。(事实上,我们的意思是我们不喜欢使用屏幕截图并只在必要时使用它。例如,我们发现它在对比屏幕小分区方面有一定价值。而且,我们有时不得不对比屏幕截图,或许是因为我们正在测试一个自绘制控件。但是更可能的情况是,我们会对比逻辑结果,而不是位图。)(一致同意)
10. 不要在测试自动化时弄丢测试点。比起查找bug,在编写脚本时犯糊涂真是太容易了。(一致同意)
测试设计
11. 对容易的东西进行自动化不是正确的策略。(一致同意)
如果你从创建一组简单的测试用例开始工作,那么你很可能在你创建强大的测试用例之前就把时间用光。一个大型、简单、且易于通过的测试用例集合会比专门的手动测试看起来更加严格,但是,一个有能力的手动测试者能在程序稳定的情况下运行日益复杂的测试。
12. 联合测试能找出新的bug。(总和比各部分要强大) (一致同意)
13. 虽然我们需要方法确定一个测试用例,但是使用不确定的(例如随机的)自动化测试也有价值。(一致同意)
我们并不鼓励盲目的测试。你需要知道你在运行什么测试。而且有时,你需要能指明准确的输入和输入序列。然而,如果你能确定程序是否通过了你在运行的测试,那将会不断带给它新的测试用例,而不是重新运行它已经通过的旧测试。
14. 我们需要规划能记录已经完成的测试的功能。(一致同意)
有些工具能容易地记录测试进程,而有些则很困难。对于调试目标和跟踪测试过程,你会想要大体知道已经运行了哪些测试用例,以及它们的结果是什么。
安排岗位和管理
15. 从发行版N(比如发行版3.0)的自动化工作中得到的大部分收益往往在发行版N+1中才能实现。但也有例外,即你在某些情况下,可以获得自动化工作的近期回报。例如烟雾测试、一些强力测试(有些强力测试只有你使用自动化后才能实现)、以及配置/兼容性测试。(一致同意)
16. 如果发行版N是你使程序自动化后的第一个发行版,那么你的发行版N的首要目标就是要为发行版N+1中的自动化编写提供支持。而你的次要目标就是轻量而有目的的测试发行版N。(一致同意)
17. 人们需要重视自动化,而不是把它视为无关紧要的任务。如果没有人专门从事这项任务,那么自动化工作将是浪费时间。(一致同意)
18. 很多测试人员是初级程序员,他们并不知道怎样构架和创建设计良好的框架。(一致同意)
数据驱动方法
数据驱动方法在主体部分已经论述过了。我相信我们都喜欢数据驱动方法,但是我们中没有一个人能在每一种可能的情况下都使用数据驱动方法。下面是会议中提出的一些具体的额外事宜。
19. 数据驱动自动化策略的主旨(数据)可以包括以下几个方面:
输入程序的参数
程序执行的操作或命令序列
驱动程序的测试用例序列
驱动程序的状态机序列
程序读取和操作的文档
系统模型(例如状态模型或因果图模型)指定的参数或事件。(一致同意)
20. 数据驱动方法具有高度可维护性,并且易于非编程人员使用。(一致同意)
然我们原则上都同意以上观点,但是我们还是能举出组织不好或思考不周的测试矩阵。如果你的设计工作做的不好,那么没有人会理解并维护你做的东西。
21. 有多个接口可以把数据输入到数据文件中,以驱动数据驱动测试。你可以选择一个,或者为不同需要和技术的测试者提供不同的接口。(一致同意)
框架驱动方法
在一段时间里,对框架的讨论变成了讨论设计过程语言以及良好实现过程语言的延伸。我们对此变得厌倦了,觉得已经有人在以前解决了这类问题,并且我们已经从我们的协议列表中删去了这个议题。我省略了沿着这条思路下去的多数要点。以下是一些针对框架的建议:
22. 是否开发框架取决于你的员工的数量和熟练程度。(一致同意)
23. 创建一个框架时,要注意你在哪个级别上创建函数。例如,你可以考虑下面三种级别之一:
菜单/命令级,执行简单命令
对象级,对具体事物实行操作
任务级,负责具体的经常反复执行的任务
你会发现主要在一个级别上工作是高效的。所以,只有当你明确了需要在其它级别上添加测试用例后,才可以这么做。
还有很多定义和区分级别的其他方法。而无论用什么方法,分析任务总是合适的。这里要注意的是,需要避免随意地从创建十分简单的测试变成创建特别冗长复杂的测试。(一致同意)
24. 被载入框架的函数库的脚本一般应该包括错误检查功能。(一致同意)
这是任何类型编程都能得到的好经验,但是对于测试代码尤其显得重要。因为,我们希望我们测试的程序出现问题,并且,为了使报告和排除故障变得容易,我们希望看到失败的最初征兆。
25. 当创建共享库命令时,就会出现处理不同编码和文档风格的问题。因为如果人们对某人的代码不满意,他们就不会使用它。(一致同意)
26. 在通过包绕函数库创建脚本时,要注意节省时间。与此类似,要注意不要创建单独的一个库。(一致同意)
函数库是一个有组织的共享函数储藏区。如果有个函数太全面,需要用户向它传递大量的参数,那么一些程序员(正在做自动化的测试者)就可能会使用他们自己特定目的的较小版本。而有的程序员由于匆忙,不在意检查库中的代码。还有的程序员则不信任库中的代码,因为他们觉得(或许是正确的)库里的大部分东西没有被测试过,存在着bug。
27. 应该把测试参数包括在数据文件中,例如.ini文件,设置文件以及配置文件,而不是把嵌入在自动化脚本或含有脚本的文件中的常量包括在内。(一致同意)
28. 包裹是一个好东西。要尽可能多的合理使用它们。
本地化
我们花了很多时间来谈论本地化,并且我们获得了一个让我吃惊的结论。我们很可能在以后的LAWST会议中继续讨论它,但是,如果你被告知,现在对基于GUI的自动化投资能使你在本地化时得到很大回报,那么会议上那些有自动化本地化测试经验的人所表达出的挫折感应该可以给你提个醒。
29. 自动化本地化测试的目的是表明先前工作的基线函数仍然可以工作。(一致同意)