转自:http://www.ibm.com/developerworks/cn/rational/r-cn-guiautotesting3/

所谓自动化测试,就是“自动化”+“测试”。自动化本身显然不是自动化测试的全部,在我们解决了测试脚本自动执行的问题之后,还是要回到测试本身,解决如何进行分析验证的问题。测试结果的分析对于测试来说非常重要,往往需要收集大量的数据,包括屏幕截图和产品日志,并根据具体需要分析各种不同的信息,例如验证点,测试实际步骤和自动测试调试信息等等。可以通过精心准备测试用例、合理输出验证信息、筛选显示测试结果、自动记录测试实际步骤等手段来全面提高分析测试结果的效率。本篇文章从准备测试用例开始,介绍了验证信息的输出,Notes 自动测试日志的各种功能,并对每项功能的实现原理做深入介绍。

准备准确的测试用例

测试用例是测试脚本的基础,不合理的测试脚本既不利于自动化,也不利于测试。而好的测试脚本则要做到描述简洁准确,操作步骤清晰,测试目的明确易懂。由于在编写测试用例时,产品一般没有完成,测试人员可能并不清楚执行用例的每个步骤,所以用例容易写得很模糊。手工测试人员执行时也许没有太多困扰,因为他了解设计文档,知道应该执行什么样的步骤,但是自动测试人员并没有这么清楚,依照模糊的用例编写测试脚本往往无从下手。所以,在开始编写测试用例之前,往往需要跟测试人员沟通,完善并得到准确的测试用例,这样才能做到有的放矢。

那么什么样的测试用例是描述准确,易于实现的?让我们通过一个测试 Notes 中的打开应用对话框的例子来看一看。

图 1. 测试打开文件对话框

例 1A. 模糊的测试用例

  1. 打开“Open Application”对话框
  2. 验证使用对话框可以打开 Server 端的用户邮箱
例 1B. 准确的测试用例
编号 步骤 验证
1 点击菜单:File-> Open Application
2 在 Server Combobox 中输入用户邮件服务器地址
3 点击“打开”按钮
4 在 File Name Editbox 中输入邮箱路径名称
5 点击“打开”按钮 验证用户邮箱成功打开

显然,对于一个有经验的测试人员,可以很轻松的理解例 1A 中的描述,但是对于自动测试人员来说,这种描述不够细致,并且容易带来歧义的。我们需要知道通过何种途经打开文件对话框?怎样切换到 Server?怎样在 server 上定位到用户邮箱应用?只有当我们把这些都明确下来之后,测试脚本才是准确的,并且易于实现的。

回页首

记录关键测试步骤和结果

准备好了用例,我们就可以开始编写测试脚本。基于准确的测试用例描述和灵活的 IBM 框架支持,我们可以轻松的把测试步骤翻译成测试脚本。同样以脚本例 1B 为例,我们可以可以得到例 2 中的脚本代码:

例 2. 初步的测试脚本
编号 步骤 验证
1 点击菜单:File-> Open Application Menu.mOpenApplication.pick()
2 在 Server Combobox 中输入用户邮件服务器地址 OpenAppDlg dlgOpenApp = new OpenAppDlg();
dlgOpenApp.getServerCombo().setText(User.mailServer)
3 点击“打开”按钮 dlgOpenApp.getOpenButton().click()
4 在 File Name Editbox 中输入邮箱路径名称 dlgOpenApp.getFileEdit().setText(User.mailFileName)
5 点击“打开”按钮 dlgOpenApp.getOpenButton().click() Logger.logCompare(User.name, MailTask.getOpenedMailOwner(),“VP: User’s mail file is opened”)

这是一个新手容易写出的测试脚本,这个脚本遵循 IBM 框架设计,完成了用例要求的步骤,并加入了对测试结果的验证。看起来符合测试用例的要求,不过,这个脚本还不够好。

我们说“自动化测试”=“自动化”+“测试”,作为程序执行正确性的验证工具,我们需要假设执行流程中的每一步都是可能出现错误的。比如说步骤一的点击菜单动作,就可能因为程序内的菜单映射错误而打开错误的对话框;再比如在步骤三中,可能因为网络超时或程序错误判断自身为离线状态而无法成功连接到服务器。还是以为回到测试结果的分析这一问题上来,一旦产品有了缺陷或者测试执行出错,我们如何快速定位问题么?很显然,测试人员不可能一直盯着测试脚本的执行,在自动化测试中,我们需要通过日志记录下关键步骤的执行过程和结果。

在通过日志分析结果时,通常我们需要知道两点:

  1. 步骤是否已执行;
  2. 被测程序是否正确做出反应。

对于第一点,通过在 App Object 层和 Task 层的增强,Notes 自动测试的框架能够自动输出信息,显示出执行了的步骤,这个功能本文后面会做深入介绍。而被测程序是否正确做出反应,则需要我们在测试脚本中进行检测并记录。实际上,当我们手工验证测试用例时,我们都会不经意的验证每一步的程序响应,并决定下一步的操作。作为“隐藏的验证点”,我们需要把他们补充到测试用例中。例 3 中给出了经过完善后的测试用例:

例 3. 完善后的测试用例
编号 步骤 验证
1 点击菜单:File-> Open Application 显示模态对话框出现,标题为“Open Application”
2 在 Server Combobox 中输入用户邮件服务器地址 验证输入结果正确
3 点击“打开”按钮 验证应用列表更新显示 Server 端应用内容
4 在 File Name Editbox 中输入邮箱路径名称 验证输入结果正确
5 点击“打开”按钮 验证“Open Application”对话框关闭
验证用户邮箱成功打开

相应的,我们在测试脚本的编写过程中也需要加入对各个步骤中程序响应的验证,并将结果记录到日志中。例 4 中给出了我们最终的示例测试脚本:

例 4. 完善后的测试脚本
  1. Logger.logDetail(“1. 点击 File-> Open Application 菜单”);
  2. Menu.mOpenApplication.pick();
  3. OpenAppDlg dlgOpenApp = new OpenAppDlg();
  4. Logger.logCompareFatal(true, dlgOpenApp.exist(LocalSettings.shortWaitTime),
  5. "验证:‘打开应用’对话框出现");Logger.logDetail("2. 在 Server Combox 中输入服务器地址");
  6. dlgOpenApp.getServerCombo().setText(User.mailServer);
  7. Logger.logCompareFatal(User.mailServer, dlgOpenApp.getServerCombo().getText(),
  8. "验证:服务器地址输入正确");Logger.logDetail("3. 切换到 Server");
  9. dlgOpenApp.getOpenButton().click();
  10. Logger.logCompareFatal(true, isContentOnServer(dlgOpenApp, User.mailServer),
  11. "验证:列表框中显示的内容为服务器上的内容");
  12. Logger.logDetail("4. 在 File Name Editbox 中输入邮箱路径名称");
  13. dlgOpenApp.getFileEdit().setText(User.mailPath);
  14. Logger.logCompareFatal(User.mailPath, dlgOpenApp.getFileEdit().getText(),
  15. "验证:邮件路径输入正确");
  16. Logger.logDetail("5. 点击确定,打开应用");
  17. dlgOpenApp.getOpenButton().click();
  18. Logger.logCompareFatal(true, dlgOpenApp.waitForNonExistence(LocalSettings.shortWaitTime),
  19. "验证:‘打开应用’对话框关闭");
  20. Logger.logCompare(User.name, MailTask.getOpenedMailOwner(),
  21. "验证:邮件应用正确打开");

Logger 类是我们在 Notes 项目中定义使用的日志工具,可以支持多层级、多类型的日志记录。在例四中,我们用到了其中的三个日志函数:

  1. logDetail(expression):记录步骤基本信息
  2. logCompareFatal(expected, actual, vp):记录步骤验证结果,如果比较失败,停止当前用例。
  3. logCompare(expected, actual, vp):记录验证点,返回 boolean 值表示比较结果

我们之所以对关键步骤验证用到 logCompareFatal,是因为这些步骤不能正确执行的话,后面的验证和后续步骤也就失去继续执行的意义。与其浪费时间执行无意义的操作,不如停止用例,及时做出错误清理,同时记录错误信息,如截屏,和输出验证信息。通过完善的步骤检查和日志记录,我们可以在脚本发现错误的时候借助日志分析直接定位到出现问题的步骤。同时,您可能注意到我们在 exists 和 waitForNonExistence 函数中使用了超时设置,可以在保证被测程序有足够响应时间的情况下,尽可能的节约测试执行时间。如果您希望了解关于执行环境清理以及步骤间等待的更多细节,请参考本系列的第二篇文章。

回页首

日志的艺术

日志对自动化测试而言至关重要,而好的日志工具可以帮助我们提高测试脚本的输出质量以及问题定位分析效率。在 Notes 项目中,我们实现了自定义的 Logger 类以支持各种特定的输出需求。下面,让我们一起来看一看日志中都可以包括一些什么样的内容。

图 2. Notes 自动测试用例日志

如图 2 所示,Notes 的日志输出是一个 html 格式的文件,提供了验证点结果,用例结果和发生错误的截图。Notes 的日志工具支持以下功能:

  1. 自动记录测试步骤 (TESTPLAN)
  2. 隐藏和显示不同级别日志的选择按钮
  3. 按照关键词筛选显示日志内容
  4. 截图链接 (IMAGE)
  5. 被测产品日志链接 (IMAGE 和 NOTES LOG FILE)
  6. 记录生成日志的位置 ("CALLSTAMP")
  7. 记录日志生成的时间 (TIMESTAMP)
  8. 记录测试环境配置信息 (SETTINGS)

自动记录测试步骤:这一功能自动记录了每一步操作的执行过程,可以帮助我们分析步骤是否已经正确执行。在下一节中,我们会详细介绍。

隐藏和显示不同级别日志的选择按钮:以往分析日志的第一个感受就是日志内容太多太杂,在分析的过程中很容易丢掉主要的线索。另外,不同的读者需要的内容不一样,比如手动测试的人只关心验证点是否通过,而自动测试人员需要知道所有测试细节。Logger 通过提供 logError, logWarning, logDebug 等函数,把日志级别信息也同时输出到日志文件中。在检索日志文件时通过点击日志文件上方的选择按钮,我们就可以隐藏或显示相应级别的日志内容。

按照关键词筛选显示日志内容:跟上一点类似,提供更灵活的方式,按关键字筛选显示日志,便于查看测试人员关心的内容。关键字不仅可以是日志内容的某一部分,还可以是类的名字。

截图的链接:一图胜千言,截图是分析日志最直观的工具。当测试出现错误和异常时,Logger 能自动截图,把图片按日志文件的目录结构保存在电脑上,并且把文件路径作为链接显示在日志中。通过截图,测试人员可以更直观的了解出现的错误和异常。实现截图的代码如下:

例 5. 实现截图功能
  1. BufferedImage capture = null;
  2. Rectangle area = new Rectangle(x, y, width, height);
  3. Robot robot = new Robot();
  4. capture = robot.createScreenCapture(area);
  5. FileOutputStream out = new FileOutputStream(filename);
  6. JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out);
  7. encoder.encode(capture);
  8. out.flush();
  9. out.close();

被测产品日志链接:产品的执行日志。自动测试日志用来让测试人员发现产品问题,而进一步,让研发人员解决问题,通常还需要产品的日志。Logger 在被测程序出现不响应和崩溃时,会主动收集被测程序的日志文件,生成链接提供给测试和研发人员。在 Notes 项目中实际应用时,由于 Notes 的每行日志都会记录当前时间,Logger 会根据测试开始和结束时间,到 Notes 的日志文件中找到测试过程中生成的日志片段,并保存到自动测试日志目录的单个文件中。这样便可以将不同测试用例造成的问题隔离出来。

记录生成日志的位置:提供日志记录点的调用函数和具体位置。在自动测试人员分析日志时,往往会不清楚日志是什么地方产生的,这样在找出问题时需要花时间搜索代码。Logger 通过调用“newThrowable().getStackTrace()”,能够找到输出日志时,程序执行到的类名,方法名和行号。这些信息被分析后输出到日志文件,成为 CALLSTAMP 列显示在日志的右边。值得说明的是,在分析调用栈时,需要得到除了 Logger 之外的第一行,因为要记录的是调用 Logger 的函数,而不是 Logger 自己的函数。以下面的调用栈为例,这里的 EnableInstanceCheck 函数才是需要被记录到日志中的,而不是 logMessage。

例 6. 待分析的调用栈
  1. Logger.logMessage()
  2. Logger.logDebug()
  3. SpellCheckTask.EnableInstanceCheck()
  4. script2.testMain(Object[])

记录日志生成的时间:顾名思义。时间对于日志分析也是非常有用的信息,通过比较每行日志记录时间有助于找出性能相关的问题,例如当两次点击被测程序的时间比较长的时候,有可能是因为在第一次点击时的性能不够好,没有及时响应。具体实现比较简单,此处不再介绍。

记录测试环境配置信息:包括测试平台,产品版本和配置信息等。这些内容在 Logger 初始化的时候由 Logger 分析输出,可以帮助我们分析定位由于平台和配置差异导致的问题。

除了通过单一日志文件显示具体每一测试用例的信息,Logger 还能为每组测试用例生成测试报告,格式如图 3 所示。它提供了每个用例的结果,通过率等信息,还包含日志文件的链接

图 3. 测试报告

回页首

自动记录测试步骤

Notes 的 Logger 能够帮助我们自动记录测试步骤,下面让我们具体看一看这一功能的作用和实现方法。首先,记录测试步骤的好处有哪些呢?对 Notes 测试来说,有如下两点:

  1. 测试人员不需要读测试代码就能知道用例的测试步骤
  2. 测试代码往往有不同的逻辑分支。有了实际测试步骤,能够知道实际运行到的代码分支
图 4. 测试步骤记录示例

图 4 是测试中实际执行到的例子。其中典型的部分是:

  1. Dialog: Replace - click on (Close) NPushButton

它表示点击了 Replace 对话框上面的 Close 按钮。短短一行日志包括下面 4 个方面的信息:

  1. 控件所在容器的名字
  2. 控件的名字
  3. 实际执行的方法名
  4. 方法接受的参数

如何自动找到这些信息?让我们先看一看这些对象是如何使用的:

例 7. 用例如何使用测试对象
  1. // 测试对象的定义
  2. class DlgReplace extends NNotesDialog{
  3.     public NPushButton getClose(){
  4.   return new NPushButton(id, this)
  5.   }
  6. }
  7.  
  8. // 用例中点击按钮
  9. NPushButton btn = new DlgReplace().getClose();
  10. btn.click();

从例 7 中控件与对话框的定义不难找到我们需要的四项信息:对话框叫 Replace, 按钮是 Close, 方法是 click,并不需要参数。问题再于如何将这些信息收集起来,并且在需要的时间输出到日志文件里。我们的解决方案分别如下:

  1. 构造控件时,记录调用栈 ( 例 8),从中找到容器和 getter 方法的名字:Replace 和 Close。之所以我们不在控件调用 click 方法时,使用控件内部的方法获得控件容器的类名,有两个原因:
    1. 容器有可能会互相嵌套。测试人员最关心的是最顶级的容器名字,比如对话框
    2. 不是所有控件构造时都传入了容器的引用。例如 web 控件通常用 xpath 定义,构造时并不需要指定容器,这样的控件没有内部方法获得控件容器的信息。
例 8. 构造控件的调用栈
  1. NControl.<init>(ControlID,String,INControl)
  2. NPushButton.<init>(ControlID,INControl)
  3. DlgReplace.getClose()
  1. 调用控件方法时,记录调用栈,从中取得第一次调用时的方法名。
例 9A. 点击按钮时的调用栈
  1. NPushButton.click(Point)
  2. NPushButton.click ()
  3. script2.testMain(Object[])
例 9B. 操作控件时的调用栈
  1. NComboBox.click()
  2. NComboBox.clearText()
  3. NComboBox.setText(String)
  4. script2.testMain(Object[])

当执行自动测试到 DlgReplace.getClose().click() 时,函数调用栈如例 9A 所示。显而易见,最后调用的 click 方法,对应了该次操作。然而大多数情况并没有这么简单,比如在文本框中输入文字时需要执行若干子步骤,包括点击控件,清除文字,和输入文字。这些步骤都在 setText 方法中调用。如例 9B 所示的调用栈。控件的方法可能会相互嵌套,只有第一次调用,才是最便于理解执行步骤的。我们在 click 函数里分析调用栈,能知道调用 click 方法是为了往控件上输入文字,然后记录成 setText。值得注意的是,如果把每个调用栈都转成步骤输入到日志的话,会包括重复并且很有歧义的信息。这就需要引入一个机制避免重复输出。我们的解决办法是每次输出时,将调用栈每行的行号都记录下来。下次输出时把当前的跟原先的比较,如果是一致,表示属于同一次操作,直接忽略掉。

  1. 记录传入的参数。这个很容易理解,比如调用 setText("hello") 时,把 hello 记录下来。

通过上述三步,我们就可以获得操作步骤的信息。有了这些信息后,需要在合适的位置调用日志函数,将步骤输出到日志中。在 Notes 项目使用的控件库中,所有 Notes 控件有一个基类,这个类定义了 click 方法,控件库的其它类都是由它继承而来。因为几乎所有的 UI 操作都会直接或间接地调用 click 方法,我们只需要在 click 函数开始的时候,组合上面说的四项信息成测试步骤,然后调用日志函数把测试步骤输出到日志文件中。在这里,我们给出的只是基本原理,您可以结合自己项目实际的特点,实现这一功能。

总结

提高测试结果分析效率和自动化测试过程同样重要。本章从测试用例的准备开始,由浅入深地介绍了如何优化测试用例,用例如何记录日志,以及日志能提供什么功能,各个功能又是如何实现。在最后,我们还介绍了自动记录测试步骤的基本原理。希望能够为您提供改进测试日志的思路和指导。在本系列的下一章,我们将会为读者介绍控件库的设计和使用,其中也会介绍录制用例和自动生成对象定义的基本原理,敬请期待。

读《实战GUI自动化测试》之:第三步,如何提高测试结果分析的效率的更多相关文章

  1. 读《实战GUI产品的自动化测试》:第一步——了解自动化测试,简单RFT的录制回放实例

    1.了解自动化测试,什么是自动化测试?(可以参数百度百科“自动化测试”) 2.了解自动化测试 * 自动化测试如何改善产品的质量 * 自动化测试无法完全替代手工测试 * 自动化测试无法发现新的问题——适 ...

  2. Knative 实战:三步走!基于 Knative Serverless 技术实现一个短网址服务

    短网址顾名思义就是使用比较短的网址代替很长的网址.维基百科上面的解释是这样的: 短网址又称网址缩短.缩短网址.URL 缩短等,指的是一种互联网上的技术与服务,此服务可以提供一个非常短小的 URL 以代 ...

  3. java入门第三步之数据库连接

    数据库连接可以说是学习web最基础的部分,也是非常重要的一部分,今天我们就来介绍下数据库的连接为下面学习真正的web打下基础 java中连接数据库一般有两种方式: 1.ODBC——Open Datab ...

  4. java入门第三步之数据库连接【转】

    数据库连接可以说是学习web最基础的部分,也是非常重要的一部分,今天我们就来介绍下数据库的连接为下面学习真正的web打下基础 java中连接数据库一般有两种方式: 1.ODBC——Open Datab ...

  5. Membership三步曲之进阶篇 - 深入剖析Provider Model

    Membership 三步曲之进阶篇 - 深入剖析Provider Model 本文的目标是让每一个人都知道Provider Model 是什么,并且能灵活的在自己的项目中使用它. Membershi ...

  6. 手机GUI自动化测试介绍

    手机GUI自动化测试介绍 Posted on 2013/05/15 Xing Binbin(测试工程师) 摘要 众所周知,自动化测试可以一定程度上减轻测试人员负担,提高测试效率,并且通过自动化还可以实 ...

  7. 第三步 用Jena自定义完成数据库到RDF的映射

    第三步 用Jena自定义完成数据库到RDF的映射 2013年10月17日 8:53:27 这一步用Jena编程,终于能做点有技术含量的事情了.这个工作计划本周内完成,下周一好给老师一个交待. 目标:把 ...

  8. 基于 SWTBot 进行 Eclipse GUI 自动化测试

    背景简介 在软件开发领域,持续集成和部署对于软件测试自动化提出了越来越高的要求,基于 Eclipse 应用在这一需求中仍然占据重要的组成部分.目前主流的测试自动化工具主要以录制回放的功能为主,辅助以脚 ...

  9. 在深谈TCP/IP三步握手&四步挥手原理及衍生问题—长文解剖IP

    如果对网络工程基础不牢,建议通读<细说OSI七层协议模型及OSI参考模型中的数据封装过程?> 下面就是TCP/IP(Transmission Control Protoco/Interne ...

随机推荐

  1. E-SATA接口

    1.VGA接口 VGA接口就是显卡上输出模拟信号的接口,也叫D-Sub接口,其实就是显示转移的接口,比如连接投影仪.连接电视等等.从外观上讲,VGA接口是一种D型接口,上面共有15针空,分成三排,每排 ...

  2. 又见古老的Typosquatting攻击:这次入侵Npm窃取开发者身份凭证

    有些攻击方式虽然听起来很幼稚,但有时候却也可以生效,比如typosquatting攻击——我们上次看到这种攻击是在去年6月份,这本身也是种很古老的攻击方式. 所谓的typosquatting,主要是通 ...

  3. 配置文件的备份和IOS 的备份

    分享到 QQ空间 新浪微博 百度搜藏 人人网 腾讯微博 开心网 腾讯朋友 百度空间 豆瓣网 搜狐微博 百度新首页 QQ收藏 和讯微博 我的淘宝 百度贴吧 更多... 百度分享 广场 登录 注册 关注此 ...

  4. Centos下mahout安装与配置

    对于Mahout的安装与配置,须要一个前提.就是hadoop已经安装. 假设没有安装能够參考. http://blog.csdn.net/u012965373/article/details/4533 ...

  5. nyoj 739 笨蛋的难题四

    笨蛋难题四 时间限制:1000 ms  |  内存限制:65535 KB 难度:3 描写叙述 这些日子笨蛋一直研究股票,经过调研.最终发现xxx公司股票规律,更可喜的是 笨蛋推算出这家公司每天的股价, ...

  6. Unity3D游戏开发最佳实践20技巧(一)

    关于这些技巧这些技巧不可能适用于每一个项目. 这些是基于我的一些项目经验.项目团队的规模从3人到20人不等. 框架结构的可重用性.清晰程度是有代价的--团队的规模和项目的规模决定你要在这个上面付出多少 ...

  7. Myeclipse10集成Flex4.6

    安装好flash builder4.6 执行fb安装文件夹下utilities\Adobe Flash Builder 4.6 Plug-in Utility.exe 插件. 第一次选择flash b ...

  8. 展开阅读全文 js 爬虫操作

    from selenium import webdriver import time import random from bs4 import * browser = webdriver.Chrom ...

  9. Ubuntu16.04下搭建开发环境及编译tiny4412 Android系统【转】

    本文转载自:http://blog.csdn.net/songze_lee/article/details/72808631 版权声明:本文为博主原创文章,未经博主允许不得转载. 1.安装ssh服务器 ...

  10. 洛谷P3216 [HNOI2011]数学作业

    题目描述 小 C 数学成绩优异,于是老师给小 C 留了一道非常难的数学作业题: 给定正整数 N 和 M,要求计算 Concatenate (1 .. N) Mod M 的值,其中 Concatenat ...