测试自动化及其框架¶
第 9 章¶
测试自动化及其框架¶
软件测试是一项艰苦的工作,需要投入大量的时间和精力,据统计,软件测试会占用整个开发时间的 40%。一些可靠性要求非常高的软件,测试时间甚至占到总开发时间的 60%。但是软件测试工作具有比较大的重复性,我们知道,软件在发布之前都要进行几轮测试,也就是说大量的测试用例会被执行几遍。在测试后期所进行的回归测试,大部分测试工作是重复的。回归测试就是要验证已经实现的大部分功能,这种情况下,只是为了解决软件缺陷、需求变化,代码修改很少,针对代码变化所做的测试相对比较少,而为了覆盖代码改动所造成的影响而要进行大量的测试,虽然回归测试找到软件缺陷的可能性小,效率比较低,但又是必要的。此后,软件产品版本不断更新,不断增加功能或修改功能,期间所进行的测试工作重复性也很高,所有这些因素驱动着软件自动化的产生和发展。
软件测试实行自动化进程,决不是因为厌烦了测试的重复工作,而是测试工作的需要,即完成手工测试所不能完成的任务,提高测试效率和测试结果的可靠性、准确性和客观性,提高测试覆盖率,保证测试工作的质量。
本章将主要介绍软件测试自动化的概念、原理和方法,如何引入和实施自动化测试以及各种类型的测试工具,帮助读者全面掌握软件测试自动化的有关知识和技能。
9.1 测试自动化的内涵¶
自动化测试 (Automated Test) 是相对手工测试 (Manual Test) 而存在的一个概念,由手工逐个地运行测试用例的操作过程被测试工具或系统自动执行的过程所代替,包括输入数据自动生成、结果的验证、自动发送测试报告等。主要是通过所开发的软件测试工具、脚本 (Script) 等来实现,具有良好的可操作性、可重复性和高效率等特点。测试自动化是软件测试中提高测试效率、覆盖率和可靠性等重要手段,也可以说,测试自动化是软件测试不可分割的一部分。
9.1.1 手工测试的局限性¶
测试人员在进行手工测试时,具有创造性,可以举一反三,从一个测试用例,想到新的一些测试场景,包括原有测试用例没有覆盖的、特殊的情况或边界条件。同时,对于那些复杂的逻辑判断、界面是否友好,手工测试具有明显的优势。但是,简单的功能性测试用例在每一轮测试中都不能少,而且具有一定的机械性、重复性,其工作量往往较大,无法体现手工测试的优越性。如果让手工做重复的测试,容易引起测试人员的乏味,严重影响工作情绪等。而且,手工测试在某些方面甚至束手无策、无法实现测试的目标,存在着一定的局限性,例如:
9.1.2 什么是测试自动化¶
谈到自动化测试,一般就会提到测试工具。许多人觉得使用了一两个测试工具就是实现了测试自动化,这种理解是不对的,至少是片面的。的确,测试工具的使用是自动化测试的一部分工作,但 “用测试工具进行测试” 不等于 “自动化测试”。那什么是 “自动化测试” 呢?
自动化测试是把以人为驱动的测试行为转化为机器执行的一种过程,即模拟手工测试步骤,通过执行由程序语言编制的测试脚本,自动地完成软件的单元测试、功能测试、负载测试或性能测试等全部工作。自动化测试集中体现在实际测试被自动执行的过程上,也就是由手工逐个地运行测试用例的操作过程被测试工具自动执行的过程所代替。自动化测试,虽然需要借助测试工具,但是仅仅使用测试工具不够,还需要借助网络通信环境、邮件系统、系统 Shell 命令、后台运行程序、改进的开发流程等,由系统自动完成软件测试的各项工作,例如:
陷生命周期等自动化处理;
(8) 测试报告自动生成功能等。¶
这样,测试自动化意味着测试全过程的自动化和测试管理工作的自动化。如果使整个软件测试过程完全实现自动化,而不需要丝毫的人工参与或干涉,这是不现实的。虽然不能完美地实现测试自动化,但是,我们理应每时每刻向这个方向努力,不断地问自己 —— 这些测试工作能否由软件系统或工具来自动完成?在测试计划、设计、实施和管理的任何时刻,始终寻求更有效、更可靠的方法和手段,以有助于提高测试的效率。所以,有人更希望将测试自动化解释成 “能够使测试过程简单并有效率、使测试过程更为快捷而没有延误的方法或努力”。从这里可以认识到,“全过程的自动化测试” 思想是非常重要的,会改变我们测试工作的思维、改变我们测试的生活,将测试带到一个新的境界。
自动化测试是相对手工测试而存在的,所以自动化测试的真正含义可以理解为 “一切可以由计算机系统自动完成的测试任务都已经由计算机系统或软件工具、程序来承担并自动执行”。它包含下列三层含义。
9.1.3 软件测试自动化的优势¶
由于手工测试的局限性,软件测试借助测试工具成为必要。自动化测试由计算机系统自动来完成,由于机器执行操作速度快,也不会劳累,可以 24 小时连续工作,而且会严格按照所开发的脚本、指令进行,不会有半点差错,所以自动化测试的优势也很明显。
正是这些特点,软件测试自动化可以弥补手工测试的不足,给软件测试带来不少益处。
(1)缩短软件开发测试周期。软件自动化测试具有速度快、永远不知疲倦等特点,对同样的上千个测试用例,软件测试自动化工具可以在很短时间内完成,还可以每周 7 天、每天 24 小时不间断运行,能不厌其烦地运行同样的测试用例十遍、一百遍等。
9.2 测试自动化实现的原理¶
软件测试自动化实现的基础是可以通过特定的程序 (包括脚本、指令) 模拟测试人员对软件系统的操作过程,如测试过程的捕获和回放,其中最重要的是识别用户界面 (User Interface, UI) 的元素以及捕获键盘、鼠标的输入,将操作过程转换为测试工具可执行的脚本;然后,对脚本进行修改和优化,加入测试的验证点;最后,通过测试工具运行测试脚本,将实际输出记录和预先给定的期望结果进行自动对比分析,确定是否存在差异。无论是对功能测试、还是对性能测试,自动化实现的方法都比较接近,只不过是功能测试侧重动能验证,而性能测试需要模拟成千上万的虚拟用户。
自动化测试也包括动态测试和静态测试,上面所介绍的是动态的自动化测试,而静态的自动化测试类似于编译系统那样,对计算机程序进行扫描、逐行检查,直接对代码进行语法分析、代码风格检查等,以发现不符合代码规范等问题。
9.2.1 代码分析¶
最早进行代码分析的工具是编译器。为了顺利地编译代码,编译器首先要检查程序是否符合编程语言的语法,能够发现代码中的语法错误,然后将源代码转换成可执行的二进制代码。但是,早期的编译器对那些语法上正确但是非常可疑的代码结构置之不理。1979 年,贝尔实验室的 Steve Johnson 在 PCC (Portable C Compiler, 轻量型 C 编译器) 基础上开发出代码分析工具 Lint,能检查出更多不符合规范的错误 (如将 “==” 写成了 “=”) 以及函数接口参数不一致性问题等,完成代码健壮性检查。Lint 后来形成一系列工具,包括 PC-Lint/FlexeLint (Gimpel) 和 Lint Plus (Cleanscape) 等。
代码分析工具还体现在集成开发环境中,多数 IDE 的代码编辑器都可以实时进行代码检查,直接定位和高亮显示警告信息和可能的错误。除了内建的静态分析外,大部分 IDE 都有可选的插件来执行更全面的代码分析,例如,Eclipse 在 “源代码分析器” 的分类列表中有多达几十种插件,这些插件包括:
(1)代码规则或者是代码风格的检查工具,例如 Checkstyle、FindBugs、JLint、PMD 等,如图 9-1 所示。
(2) 检查和移出冗余代码的分析器,如 Duplication Management Framework。

例如,开源的代码分析器 PMD 能分析以下包含风险的代码。
再举一个例子,我们可能会因为数据库连接未关闭问题焦头烂额,例如,资源未能在 try/catch/finally 块中被释放、清理。而使用 JUnit 可以分析 Java 类的结构和内容,检查它们与既定规则的匹配程度。例如,规则可能是这样的:若在某个方法体中创建或从连接池中取得了数据库连接,那么必须保证存在一个 try/catch/finally 块,且在 finally 块中关闭了连接或释放了连接。
9.2.2 对象识别¶
测试工具能够实现对用户界面的操作,要么就按照屏幕的实际像素坐标来定位,要么通过寻找 UI 上的对象 (如窗口、按钮、滚动条等) 来确定操作的目标。前者方法虽然简单,但生成的脚本缺乏可读性,不容易维护,而且在不同的屏幕分辨率下脚本可能根本不能运行,所以越来越多的测试工具选择对象识别方法。GUI 对象的识别工具比较多,微软 Visual Studio 中就包含 Spy++, 它可以用来识别各种 Windows 的 GUI 对象,如图 9-2 所示。

要识别对象,就是获得 UI 对象的 ID、对象名,然后根据对象的 ID、对象名,确定其属性值等数据。基于 GUI 对象识别和控制的自动化测试工具,在脚本语言中一般使用 Windows User Interface (用户界面) 一类的 API 调用来识别、操作 GUI 对象。Windows UI API 函数封装了操作应用软件所需的接口函数,包括键盘和鼠标的捕获,以及窗口、按钮、选择项等的识别和操作。例如,以窗口类 API 为例,包括几十个函数,例如:
HWND GetDesktopWindow(VOID)
HWND GetForegroundwindow(VOID)
HWND GetTopWindow(HWND hWnd);
BOOL GetWindowRect(HWND hWnd, LPRECTlpRect);
Int GetWindowText(HWND hWnd, LPTSTR lpString, Int nMaxCount);
Int GetClassName(HWND hWnd, LPTSTR IpClassName int nMaxCount);
BOOL GetClassInfoEx(HINSTANCE hlnst, LPCTSTR lpszClass, lpwcx);
DWORD GetWindowThreadProcessId(HWND hwnd, LPDWORD lpdwProcessId);
BOOL IsWindowVisible(HWND hWnd);
HWND FindWindow(LPCTSTR IpClassName, LPCTSTR IpWindowName);
除了 Windows API 函数调用方法之外,还有其他一些技术可以采用,如反射机制 (Reflection)。通过反射来加载被测试程序,获取被测试程序的各种属性,触发被测试程序的各种事件,从而达到自动化测试的目的。在 C#、C++ 和 Java 程序语言中都提供了反射机制,增加了这些非动态语言的动态性,可以在程序运行时动态地创建类的实例,并绑定到现有对象,然后调用类的方法或访问其字段和属性,这也为自动化测试提供了一种获取对象信息的途径。例如,在 C# 中使用静态方法 GetType 获取变量类型。
// Using GetType to obtain type information:
int i = 42;
System.Type type = i.GetType();
System.Console.WriteLine(type);
输出为:System.Int32
下面的示例使用反射获取已加载的程序集的完整名称:
// C# : Using Reflection to get information from an Assembly:
System.Reflection.Assembly o = System.Reflection.Assembly.Load("mscorlib.dll");
System.Console.WriteLine(o.GetName());
输出为:mscorlib,Version=2.0.0.0,Culture=neutral,PublicKeyToken=b77a5c561934e089
9.2.3 脚本技术¶
脚本是一组测试工具执行的指令集合,也是计算机程序的一种形式。脚本可以通过录制测试的操作产生,然后再做修改,这样可以减少脚本开发的工作量。当然,也可以直接用脚本语言编写脚本。测试工具脚本中可以包含数据和指令,并包括:
脚本的技术围绕着脚本的结构设计,实现测试用例,在建立脚本的代价和维护脚本的代价中得到平衡,并从中获得最大益处。
脚本技术不仅用在功能测试上,来模拟用户的操作然后进行比较,而且可以用在性能、负载测试上,模拟并发用户进行相同或不同的操作,以给系统或服务器足够的负载,以检验系统或服务器的响应速度、数据吞吐能力等。
脚本可以分为线性脚本、结构化脚本、数据驱动脚本和关键字驱动脚本。线性脚本是最简单的脚本,如同流水账那样描述测试过程,一般由自动录制得来;而结构化脚本是对线性脚本的加工,类似于结构化设计的程序,是脚本优化的必然途径之一。而数据驱动脚本和关键字驱动脚本可以进一步提高脚本编写的效率,极大地降低脚本维护的工作量。目前,大多数测试工具都支持数据驱动脚本和关键字驱动脚本。在脚本开发中,常常将这几种脚本结合起来应用。
1. 线性脚本¶
线性脚本是录制手工执行的测试用例得到的脚本,这种脚本包含所有的击键、移动、输入数据等,所有录制的测试用例都可以得到完整的回放。对于线性脚本,也可以加入一些简单的指令,如时间等待、比较指令等。线性脚本适合于那些简单的测试 (如 Web 页面测试)、一次性测试,多数用于脚本的初始化 (录制的脚本用于以后修改), 或者用于演示等。
2. 结构化脚本¶
类似于结构化程序设计,具有各种逻辑结构,包括选择性结构、分支结构、循环迭代结构,而且具有函数调用功能。结构化脚本具有很好的可重用性、灵活性,所以结构化脚本易于维护。

3. 数据驱动脚本¶
数据驱动脚本,将测试脚本和数据分离开来,测试输入数据存储在独立的 (数据) 文件中,而不是存储在脚本中。针对某些功能测试时,操作步骤是一样的,而输入数据是不一样的,相当于一个测试用例对应一组输入数据。这样,同一个脚本可以针对不同的数据输入而实现多个测试用例的自动执行,提高了脚本的使用效率和可维护性。在实现上,一般都在脚本中引入变量,通过变量来引用数据,脚本本身描述测试的具体执行过程。
在实际测试当中,这种情况很多,例如,用户登录的功能测试中,“用户名、口令” 就是输入数据,测试时需要对不同的情形分别测试,如用户名为空、口令为空、大小写是否区分、是否允许特殊字符等。更理想的数据驱动脚本,可以控制测试的工作量,即控制业务操作过程,真正地由数据来驱动测试,使自动化测试具有一定的智能性。关键字驱动脚本是控制单个具体的 “动作”, 而数据驱动是控制 “过程”, 即业务层次上的操作。
测试数据列表 (Datable)
| 序号 | 用户名 | 口令 |
| 1 | Test | Pass1 |
| 2 | test sp | pass1 |
| 3 | test | pass 1 |
| 4 | test | P@ss! |
| ... ... |
数据驱动脚本示例
For i = 1 to Datatable.GetRowCount
Dialog("Login").WinEdit("AgentName:").SetDataTable("username", dtGlobalSheet)
Dialog("Login").WinEdit("Password:").SetDataTable("passwd", dtGlobalSheet)
Dialog("Login").WinButton("OK").Click
datatable.GlobalSheet.SetNextRow
Next
4. 关键字驱动脚本¶
关键字驱动脚本 (Keyword-Driven 或 Table-Driven Testing script), 看上去非常像手工测试的用例,脚本用一个简单的表格来表示,如表 9-1 所示。关键字驱动脚本,是数据驱动脚本的逻辑扩张,实际上是封装了各种基本的操作,每个操作由相应的函数实现,而在开发脚本时,不需要关心这些基础函数,直接使用已定义好的关键字,这样的好处是脚本编写的效率会有很大的提高,脚本维护起来也很容易。而且,关键字驱动脚本构成简单,脚本开发按关键字来处理,可以看作是业务逻辑的文字描述,每一个测试人员都能开发,这就能做到 “全民皆兵”—— 每个测试人员都可以进行自动化测试的工作。
当然,可以在这基础上对底层命令进行封装,形成更高层次上 (服务或业务层次) 的关键字。关键字的层次处在合适的水平,既不要关注细节,也不能过高。如果关键字过于复杂,包罗万象,就不够灵活,甚至无法适应业务逻辑的变化,反而给脚本维护带来巨大的工作量。
表 9-1 关键字驱动脚本示例 (SeleniumHTML 格式脚本)
| 命令(关键字) | 对象(操作对象) | 值(属性) | 注释 |
| open | /config/login_verify2.src=yc&.intl= cn&.partner=&.done=http%3a//cn. calendar.yahoo.com/ | 访问雅虎日历站点:http:// cn.calendar.yahoo.com/ | |
| Type | username | test1 | 输入用户名“test1” |
| Type | password | 1234567 | 输入密码“1234567” |
| clickAndWait | //input[@value='登录'] | 单击“登录”按钮 | |
| verifyTextPresent | “登出,我的账户” | 验证用户登录成功 |
9.2.4 自动比较技术¶
自动执行测试脚本时,预期输出是事先定义的或插入脚本中,然后在测试过程中运行脚本,将捕获的结果和预先准备的输出进行比较,从而确定测试用例是否通过。所以,自动比较技术在软件测试自动化中不可缺少的。
简单比较,就是对执行过程中输出的数值和期望获得的数值进行比较,例如,进行 \(5 \times 6\) 乘法运算的期望结果是 30, 脚本执行时模拟计算器程序输入 “5”、“×”、“6” 之后,单击 “=”, 其结果显示 30, 说明验证通过。当然,有更复杂的比较,如比较文件名、文件大小、文件内容,还有 Windows 窗口或控件的属性,甚至比较整个屏幕或屏幕上某个区域图像等。
图片或自绘窗口特效的验证是自动化测试中的一个难点。虽然有些自动化测试工具提供了验证图片的功能,但是稳定性都不是很好。一般图片验证原理是首先截取并保存正确的图片,然后将脚本运行时截取的图片与保存的图片进行比较。由于这种比较是在像素级上进行,极微小的差异都会被认为是不同的,这可能导致 —— 同样的脚本在不同物理机器 (显示卡、操作系统等不同) 上运行时,常常会因为显示上的微小差异而导致检查结果失败,但用户是可以接受的。
有的测试工具可以设定阈值,允许存在微小的差异,高于阈值的,被认为 “差异明显存在”, 认定验证失败;而低于或等于阈值的差异将被忽视,认定验证通过。这样,测试结果会比较稳定、可靠。如果阈值可以根据实际情况或用户的特定要求进行自动调整,那么比较技术具有一定的智能性,这种自动比较技术,可以称为 “智能比较”。例如,要求比较 (验证) 包含日期信息和数据的输出报表,是比较困难的,因为输出报表中的日期和数据都是动态的。这时,可能需要智能比较,可能要针对日期格式和数据特征来进行比较。当然,为了确认数据的正确性或为了使结果具有良好的可靠性,需要精心设计,自动产生所需的测试数据,从而根据预先准备的测试数据,采用另外一种方法来获得期望的结果,然后与实际测试结果进行比较。
在软件自动化测试脚本中,一般存在两类比较模式 —— 验证 (Verify) 和断言 (Assert), 其比较能力是相近的,Assert 命令都有对应的 Verify 命令,但对验证结果的处理是不一样的。
(1) 当 Assert 失败时,则退出当前测试;
(2) 当 Verify 失败时,测试会继续运行。
例如,下面将要介绍的 Web 功能测试工具 Selenium, 就有十几个用于自动比较的命令,即:
9.2.5 测试自动化系统的构成¶
在进行自动化测试时,最简单的情况就是在单台测试机器上运行测试工具,由这台机器执行存储在本机上的测试用例,即向被测试的软件系统发送请求或操作命令,并显示测试过程,记录测试结果。但在大规模的自动化测试过程中,靠一台测试机不能完全解决问题,需要多台机器协助工作,而且还需要调度、控制这些测试机器,以及需要特定的服务器用于存储和管理测试任务、测试脚本和测试结果。这时,需要系统地解决自动化测试框架及其环境问题。
自动化脚本的开发可以看作类似于软件开发的工作,它需要相应的集成开发环境。所以,在讨论自动化测试系统时,着重考虑自动化测试执行的环境,也就是构成自动化系统的基本框架。作为测试自动化的基本结构,可以看作由下面 6 部分组成,如图 9-3 所示。

理想的测试工具可以在任何一个路径位置上运行,可以到任何路径位置去取得测试用例,同时也可以把测试的结果输出放到任何的路径位置上去。这样的设计,可以使不同的测试运行能够使用同一组测试用例而不至于互相干扰,也可以灵活使用硬盘的空间,并且使备份保存工作易于控制。
同时,软件自动测试工具必须能够有办法方便地选择测试用例库中的全部或部分来运行,也能够自由地选择被测试的产品或阶段性成果作为测试对象。
9.3 测试自动化的实施¶
根据测试的要求和任务,来决定选择什么样的测试工具。对于一些特殊的应用,特别是一些应用服务器的功能测试,没有测试工具选择,需要自己开发新的、特定的测试工具。在多数情况下,选用开源测试工具或第三方专业软件测试工具厂家的产品是一种比较明智的方法。
在选择测试工具之前,需要对测试工具有一个总体的了解,包括有哪几类测试工具、有哪些工具可供选择。然后,进一步了解选择的标准是什么,以及如何做出正确的决策。
9.3.1 测试工具的分类¶
软件测试工具种类很多,既有商业版本,也有免费的开源版本。有时候,也根据软件应用领域来划分测试工具,包括 Web 测试工具、嵌入式测试工具等。但一般来说,会按以下两个方面来进行分类。
在平时应用时不会太在乎是白盒测试工具还是黑盒测试工具、是动态测试工具还是静态测试工具,关键是解决问题,人们往往关心是解决了功能测试还是解决了性能测试,即更会关心工具所能完成什么样的测试工作,所以在后面各节将按功能测试工具、性能测试工具等分类来进行详细的讨论,而单元测试工具已在第 5 章中讨论过,本章将不再讨论。
1. 白盒测试工具¶
白盒测试工具是针对程序代码、程序结构、对象属性、类层次等进行测试,测试中发现的缺陷可以定位到代码行,单元测试工具多属于白盒测试工具。针对白盒测试工具,可以进一步划分为静态测试工具和动态测试工具,但像 Parasoft 公司的 Jtest 和 \(\mathrm{C}++\) Test,既是静态测试工具,也是动态测试工具。
2. 黑盒测试工具¶
黑盒测试工具,一般是通过图形用户界面 (Graphical User Interface, GUI) 来实现自动化测试,即利用脚本的录制 (Record)/ 回放 (Playback), 模拟用户的操作,然后将被测系统的输出记录下来同预先给定的标准结果比较。黑盒测试工具一般应用于系统的功能测试和负载测试、性能测试等,复用性比较好,适合进行大规模的回归测试和各种性能测试。如 GUI 功能测试工具的代表有 HP 公司的 Quick Test Professional (QTP)、IBM Rational Functional tester、Parasoft 公司的 WebKing、Microfocus 公司的 SilkTest 等;性能测试工具有 HP 公司的 LoadRunner、IBM Rational Performance tester 等。
9.3.2 测试工具的选择¶
要选择好测试工具,首先就要根据软件产品或项目的需要,确定要用哪一类的工具,是白盒测试工具还是黑盒测试工具?是功能测试工具还是负载测试工具?即使在特定的一类工具中,还需要从众多不同的产品中选择合适的工具。测试工具的选择是测试自动化的一个重要步骤之一,选择一个产品,不外乎针对自己的需求、不同产品的功能、价格、服务等进行比较分析,选择比较适合自己的、性能价格比好的两三种产品作为候选对象。
在选择测试工具时,需要关注工具自身的特性,即具备哪些功能,功能强大的工具会得到更多的关注。当然也不是说,功能越强大越好,在实际的选择过程中,预算是基础,解决问题是前提,质量和服务是保证,适用才是根本。为不需要的功能花钱是不明智的,够用就可以了。同样,仅仅为了省几个钱,忽略了产品的关键功能或服务质量,也不能说是明智的行为。
在引入 / 选择测试工具时,不仅要考虑性能价格比、产品的成熟度,还要考虑测试工具引入的连续性,也就是说,对测试工具的选择必须有一个全盘的考虑,分阶段、逐步地引入测试工具。一般来说,测试工具的选择步骤如图 9-4 所示。

9.3.3 测试自动化普遍存在的问题¶
对测试自动化及其工具所能发挥的作用,大家都已经了解并认可了,但是很多软件公司在实施自动化测试时并没有达到预期效果,没有让测试工具发挥应有的作用。这里面的原因比较多,主要有以下几个方面。
1. 不正确的观念或不现实的期望¶
没有建立一个正确的软件测试自动化的观念,或操之过急,或认为测试自动化可以代替手工测试,或认为测试自动化可以发现大量新缺陷等。多数情况下,对软件测试自动化存在过于乐观的态度、过高的期望,人们都期望通过这种测试自动化的方案能解决目前所有遇到的问题,而同时测试工具的软件厂商自然会强调有利的或成功的一面,可能对要取得这种成功所要做出持久不懈的努力,却只字不提。结果最初的期望却没有实现,自动化测试也就不了了之。
2. 缺乏具有良好素质、经验的测试人才¶
有些软件公司舍得花几十万元去买测试工具软件,却不愿意给出有竞争力的薪水来招聘具有良好素质、经验丰富的测试人才。软件测试自动化并不是简简单单地使用测试工具,而需要基于测试工具开发大量的测试脚本,并在脚本开发中不断调试、修改、完善脚本。这就要求测试人员不仅熟悉产品的特性和应用领域、熟悉测试流程,而且需具备良好的编程能力和经验。
3. 测试工具本身的问题影响测试的质量¶
一般不会针对自动化测试脚本进行大规模的测试,所以自动测试的脚本的质量往往依赖于测试人才的经验和工作态度。而通过自动测试工具执行的测试用例是不需要再进行手工测试的。如果自动测试工具的质量得不到保证,将直接影响到测试结果的正确性。
如果软件测试工具没有发现被测软件的缺陷,并不能说明软件中不存在问题,而可能是测试工具本身不够全面的问题或测试的预期结果设置不对。第一次进行自动化测试时,可以结合手工测试的结果,进行对比分析和相互验证,这样做是有益的,甚至是必要的。
4. 没有进行有效的、充分的培训¶
人员和培训是相辅相成的,如果没有良好的、有效的、充分的培训,测试人员对测试工具了解缺乏深度和广度,会导致其使用效率低下,其应用结果不理想。这种培训是一个长期的过程,不是通过一两次讲课的形式就能达到效果。而且,在测试工具的实际使用过程中,测试工具的使用者可能还会遇到各种问题,这也需要有专人负责解决,否则,会严重影响测试工具使用的积极性。
5. 没有考虑到公司的实际情况,盲目引入测试工具¶
有一点很明确,不同的测试工具面向不同的测试目的、具有各自的特点和适用范围,所以不是任何一个优秀的测试工具都能适应不同公司的需求。某个公司怀着美好的愿望花了不小的代价引入测试工具,一年以后,测试工具可能成了摆设。究其原因,就是没有能够考虑公司的实际情况,不切实际地期望测试工具能够改变公司的现状,从而导致失败。
例如,国内多数软件公司是面向最终用户的一次性项目的软件开发,而不是软件产品的开发。项目开发周期短,不同的用户需求不一样,而且在整个开发过程中需求和用户界面变动较大,这种情况下就不适合引入大规模的功能测试工具。对于变化不停的需求和界面,开发和修改脚本的工作量很大,不能像产品开发中多次重复运行脚本,缺乏良好的复用性,自动化测试收益低。运用测试工具不但不能减轻工作量,反而加重了测试人员的负担。这种情况下可以考虑引入白盒测试工具,特别是静态测试工具,直接通过代码扫描发现问题,规范代码,提升代码的质量。
6. 没有形成一个良好的使用测试工具的环境¶
测试工具应用环境需要测试流程和管理机制做相适应的变化,也只有这样,测试工具才能真正发挥作用。例如,对于基于 GUI 的自动化测试来说,产品界面的改变对脚本的正常运行影响较大。再者,单元测试主要由开发人员自己完成,如果没有流程来规范开发人员的行为,在项目进度压力比较大的情况下,开发人员很可能就会有意识地将工具不好用作为借口而不做单元测试。所以,有必要在开发和测试的流程中明确如何使用测试工具,如在项目各个里程碑所提交的文档中必须包含某些测试工具生成的报告,如集成测试时 DevPartner 工具生成的测试覆盖率报告、Logiscope 生成的代码质量报告。
7. 其他技术问题和组织问题¶
自动化测试脚本的维护工作量很大,而且软件产品本身代码的改变也需要遵守一定的规则,从而保证测试脚本良好的可复用性,也就是说测试自动化和软件产品不能隔离开来,而是保持同步、相互匹配的关系。
其次,提供软件测试工具的第三方厂家,对客户的应用缺乏足够理解,很难提供强有力的技术支持和具体问题的解决能力。也就是说,软件测试工具和被测试对象 —— 软件产品或系统的互操作性会存在或多或少的问题,加之技术环境的不断变化,所有这些对测试自动化的应用推广和深入,都带来很大的影响。
9.3.4 自动化测试的引入和应用¶
在全面启动软件测试自动化之前,首先要建立对软件测试自动化正确的认识观。虽然软件测试自动化具有很多优点,可以提高测试效率、覆盖率和可靠性等,但它只是对手工测试的一种补充,软件测试自动化绝不能代替手工测试,它们各有各自的特点和优势,其测试对象和测试范围都不一样。
(1)在系统功能逻辑测试、验收测试、适用性测试、涉及人机交互性测试时,多采用黑盒测试的手工测试方法。
(2)单元测试、集成测试、系统负载或性能测试、稳定性测试、可靠性测试等比较适合采用自动化测试。
(3) 对那种不稳定软件的测试、开发周期很短的软件或一次性的软件等不适合测试自动化。
(4) 工具本身并没有想象力和灵活性,根据经验报道,自动化测试只能发现 \(15\% \sim 30\%\) 的缺陷,而手工测试却可以发现 \(70\% \sim 85\%\) 的缺陷。所以功能测试工具的准确含义是回归测试工具,因为工具不能发现更多的新问题,但可以保证对已经测试过的部分可以进行全面的验证。
多数情况下,手工测试和自动化测试相结合,以最有效的方法来完成测试任务。
1. 找准测试自动化的切入点¶
不管是自己开发测试工具,还是购买第三方现成的工具产品,当开始启动测试自动化时,不要希望一下子就能做很多事情,可以从最基本的测试工作切入,如验证新构建的软件包 (Build) 是否有严重的或致命的问题,即验证当前软件包的基本功能是否正常工作。也可以先只就某一个简单模块开始自动化测试工作,确保自动化测试的收益。如果这个模块的自动化测试成功了,再向其他模块全面推进。
2. 把测试开发纳入整个软件开发体系¶
为了良好实施自动化测试,在产品软件设计阶段就应该很好地考虑自动化测试的要求,保证软件的可测试性,软件系统中每个元素的可存取性。另外,测试脚本也应看作程序,所以应该要遵守已有的、规范的编程标准和规则,并纳入整个开发过程中。测试脚本也需要依靠配置管理来提供良好的开发环境,同时必须与所开发软件的构建紧密配合。
只要是程序,就同样会存在缺陷,就需要完成相应的脚本复查、调试和验证的过程。这并不是要求针对测试脚本还要进行大量测试,从而进入 “产品测试→产品测试的脚本测试→脚本测试的脚本测试→……” 递归的死胡同。相对来说,测试脚本语句、结构和逻辑都简单,一般通过复查就能发现脚本中的大部分问题。另外,在执行脚本时,脚本的问题也会暴露出来。在脚本执行中发现的问题,要么是被测试的软件产品的问题,要么是测试脚本自身存在的问题,通过仔细检查脚本,相对容易区分究竟是哪边出了问题。
为了使测试自动化的脚本能多次重复进行,测试脚本如同代码一样,需要版本控制,通过类似于 CVS、SubVersion 这样的工具来实现动态的配置管理。
3. 测试自动化依赖测试流程和测试用例¶
不管是手工测试还是自动化测试,关键是测试流程的建立和测试用例的设计,只有在良好的测试用例基础上,编写测试脚本,执行测试或运行测试脚本,才能保证测试的执行效果。为了适合自动化测试脚本的开发,可以将测试用例转化为矩阵、表格或其他结构化形式,使测试用例的逻辑更清晰、覆盖面更清楚,提高脚本的开发效率。
4. 软件测试自动化的投入较大¶
由于软件测试自动化在前期的投入要比手工测试的投入大得多,除了购买软件测试工具所投入的资金和人员培训成本之外,还要用很多时间去开发和维护测试脚本。
5. 进行资源的合理调度¶
理想情况下,从写第一行代码时就开始进行每日软件包构建、每日自动化的基本验证测试。这种做法能使软件的开发状态得到频繁的更新,能够及早地发现设计和集成的缺陷。为了充分利用时间与设备资源,下班之后进行自动的软件包构建、验证,紧接着自动执行测试。如果安排得好,第二天上班时,测试结果就已经发送到测试工程师的电子邮箱里了。白天开发和调试脚本,晚上执行自动化测试,这是一种行之有效的方法。
9.4 功能测试工具特性要求¶
基于 GUI 的功能测试工具在软件测试自动化中有着重要的一席之地,其基本原理是:将操作应用程序的各种动作和输入记录下来,包括键盘操作、鼠标单击等捕捉 (Record) 下来,生成一个脚本文件,这个脚本以后可以被 “回放 (Playback)”, 也就是能重复上一次所操作的动作,实现自动运行。但自动运行还不是测试,只是操作演示。没有验证,就不能算是测试。所以需要对相应的运行结果进行验证,在自动化测试脚本中插入检查点 (Check Point) 或验证点 (Verification Point, VP) 来完成实际结果和期望结果的比较。在实际工作中,往往直接在开发平台上创建脚本,或在录制脚本的基础上进行修改,使脚本更容易维护和复用。脚本需要不断运行,经过多次调试和修改,才确保脚本的执行达到预期效果。一旦完成测试脚本,就可以重复运行。
为了更有效地开发测试脚本,提高脚本的可维护性,不仅需要良好的集成开发环境的支持,与软件构建工具(如 Ant、Maven 等)无缝集成,而且测试脚本要支持数据驱动、关键字驱动、对象映射等特性。为了更全面地说明功能测试工具的特性,这里以 HP QuickTest Professional (QTP) 为例 (如图 9-5 所示),介绍其主要特性。

1. QTP 的主要特性¶
数能够为测试提供更强大的功能,如 Windows 程序中对 DLL 文件的访问、对数据库编程接口的调用等。举一个很简单又很实用的例子,完成向数据库插入一条记录的操作,程序可以提示插入成功,但数据是否正确写入数据库中,通常需要手工去数据库里进行检查,以确认功能是否真正地得到实现。如果能够在测试脚本中插入检查点,通过调用数据库提供的编程接口检查刚才的操作是否执行正常,这样就无须人工检查,测试程序可以一气呵成完成一系列的自动校验。
(3) 支持录制和回放的功能。支持对象标识和坐标标识两种方式。
(4)提供对象识别工具 (Object Spy),用来查看实时对象或测试对象的属性和方法。测试工具必须能够在程序界面中区分各种对象(如窗口、按钮、滚动条等)并识别出来,录制的测试脚本才具有良好的可读性、修改的灵活性和维护的方便性。
(5)支持多种方法来识别对象,如标准的属性 (Mandatory)、辅助的属性 Assistive、智能的属性 (Smart Identification) 和顺序标识 (Ordinal Identifier) 等。
(6) 支持抽象层和对象库 (Object Repository), 应用程序中所有对象可以统一被管理起来,测试脚本执行时,可以查找和使用对象库中的相应对象。当系统对象发生变化时,只要在对象库中做一次更新,而脚本无须做任何改动。这就是通过抽象层来实现的,即将程序界面中存在的所有对象实体一一映射成逻辑对象,测试就可以针对这些逻辑对象进行,而不需要依赖于界面上元素的变化,以减少测试脚本建立和维护的工作量。抽象层在一些测试工具中被称为测试映射图 (Test Map)、测试帧 (Test Frame) 或取值映射图 (Get Map)。举个例子,就比较容易理解抽象层的作用。如程序中经常有的用户登录窗口,一般需要输入用户名和口令,可以在脚本中将这两条信息标识为 “NAME” 和 “PASSWORD”, 对应程序中这两个变量名。这就是所建立的抽象层,可以在大量脚本里,有同样的信息标识去处理不同的用户登录操作。如果下一版本的程序中,登录窗口中两条输入信息的标识变成了 “USERNAME” 和 “PASSWORD”, 这时就不需要把所有脚本都修改一遍,只要简单地将抽象层中这两个对象的标识进行修改就可以了。脚本执行时通过抽象层自动会使用新的对象标识。有些工具软件支持程序界面对象自动搜索,建立所发现对象的抽象层,当然也可以用脚本手工编程定义所需要的抽象层。
(7) 支持数据驱动测试 (Data-Driven Test)。提供 Excel 形式的数据表格 DataTable, 用于存放测试数据或参数,并支持 Global 和 Local 两种类型。在数据驱动测试中,测试脚本通过从事先准备的数据库或文件中读取数据,在执行测试过程中将结果数据写入数据库或文件中,或直接将结果和事先保存在数据库中的预期结果值进行比较。这有利于测试脚本的代码和数据输入分离,减少代码的编程和维护工作量,也有利于测试用例的扩充和完善。
(8) 支持关键字驱动测试 (Keyword-Driven Test)。通过动作 (Action) 来组织测试用例,可以看作是脚本的关键字模式的具体表现。动作可以拥有自己的测试数据表 (DataTable) 和对象库,并支持输入 / 输出 (Input/Output) 参数。测试用例可以由若干个动作来构成,并能通过关键字视图查看和删除。其动作的调用支持三种方式:插入一个新动作、复制一个已有的动作和直接调用已存在的动作。
(9)脚本编辑器支持两种视图 —— 专家 (Expert) 模式和关键字模式。专家模式就是代码视图,而关键字模式提供一个与代码无关的、描述近似于原始测试用例的视图。借助关键词视图,容易插入、修改、数据驱动和移除测试步骤,而且通过每一步骤的活动屏幕显示被测应用的确切状态。
(10) 支持描述性编程 (Description Programming), 用简单的英语以文档形式记录每个步
骤,并通过活动屏幕将文档与一个集成截屏相结合。
(11)支持各种类型的验证点,并可以自动引入检查点来验证应用的属性和功能点,如确认输出量或检查链接的有效性。允许使用几种不同类型的检查点,包括文本、GUI、位图和数据库等。
(12) 设置环境变量 (Environment Variables), 从而使一个测试任务中所有动作共享。环境变量分为内建的 (Build In) 和自定义的 (User Defined) 两种类型。自定义的环境变量可以指向一个 XML 文件。
(13) 错误现场恢复 (Recovery Scenario), 可在前一个用例执行出错后,将被测系统恢复到初始状态,从而继续执行下面的测试用例。
(14)测试结果有多种状态,如通过 (Passed)、失败 (Failed)、完成 (Done)、警告 (Warning) 和信息 (Information) 等,并能进行过滤。
(15)提供调试环境。如 TestFusion 报告列出在测试中发现的差错和出错的位置,可快速隔离和诊断缺陷,具有调试功能,支持脚本单步运行、设置断点、得到变量返回结果等,从而有效地对测试脚本的执行进行跟踪、检查,迅速定位问题。
(16) 对外提供了大量的 API 和对象,从而可以编写脚本以实现测试的操作、配置、运行和管理完全自动化。测试工具的引入是一个长期的过程,应该是伴随着测试过程改进而进行的一个持续的过程,因此,测试工具的集成能力也是必须考虑的因素。这里的集成,包括两个方面的要求,一是能否和开发工具进行良好的集成;二是能否和其他测试工具进行良好的集成。
(17) 提供了很多插件,如. NET、Java、SAP、终端模拟器 (Terminal Emulator) 等,以支持不同类型应用的测试。
2. 其他一些需要的特性¶
(1)容错处理机制。对于可以自动执行的测试任务,通常会在上班时间将任务定制好,下班后启动任务执行,第二天上班再来检查测试执行的结果。但是,经常会出现本来需要执行整晚的工作,第二天却发现才执行了 5 分钟,就因为程序的一些异常错误而终止了,而且这种情况经常发生。因此,测试工具应有相应的容错处理系统,可以自动处理一些异常情况而对系统进行复位,或者允许用户设置是否可以跳过某些错误,然后继续执行下面的任务。例如,开源的 Web 功能测试工具 Selenium 就设定了不同的断言方式,其中当 “verify 断言方式” 验证失败时,测试执行不会终止,继续执行下去,并将错误记入日志。
(2) 命令行方式运行测试脚本,可以为测试的执行带来更大的灵活性,这样程序 Build 后就可以自动启动测试脚本的执行,并且可以同时向一组机器发布命令,让它们同时运行不同的测试脚本。
(3) 分布式测试的支持。在互联网上有许多协同工作、通信等方面的应用软件,如网上会议系统、聊天系统等,支持多用户来共同操作软件,这时对自动化测试工具有更高的要求。例如,测试任务在多台机器上运行,操作步骤之间有依赖性,如需要等到机器 A 的某操作执行完之后,机器 B 的某一步操作才能执行。这样,就要求脚本的运行能够按照事先设置的任务执行时间表进行,即在指定时间、指定设备上执行指定的测试任务。而且,还要避免资源使用冲突问题,例如,当两个测试任务要同时打开一个文件时,能保持协调或协同处理,避免出现资源竞争问题。
(4)支持远程代理程序的运行,在测试人员的工作机上可以控制实验室里的远程测试机,让测试机执行测试脚本,完成测试任务。代理程序占有很少资源、具有很强的独立性,相互传送的数据量也要很小,这样对测试结果的影响才可以不计。
(5) 跨平台特性,如支持 Windows、Mac OS 和 Linux 不同的平台,具有更广泛的应用空间。
9.5 性能测试工具特性要求¶
性能测试工具执行测试的过程一般是通过虚拟用户生成器录制关键业务操作,自动生成原始的测试脚本。然后,在控制器编辑、组织测试脚本,分发给每个负载生成器(也称代理,Agent),Agent 向服务器发送行请求模拟客户端,执行脚本的同时将测试的结果返回给控制器。最终由控制器统计测试结果,并完成测试报告。
作为性能测试工具,首先能模拟实际用户的操作行为,记录和回放多用户测试中的事务处理过程,自动生成相应的测试脚本。其次,能针对脚本进行修改,增加逻辑控制,完成参数化和数据关联。假设要使用一系列不同的输入值来执行相同脚本的操作时,来匹配多个实际用户的操作行为,从而反映出系统真正的负载能力,这就需要用参数来替换已录制的值,即脚本参数化。脚本的参数化可以简化脚本,并增强脚本适用性。数据关联类似于参数化,可以简化脚本,适应企业应用中需要动态数据的情况。数据关联包含三个步骤 —— 定义哪个录制的值需要被关联、定义数据源和它们之间的关联关系。
再者,可以设置不同的应用环境和场景,通过虚拟用户执行相应的测试脚本。最后,在脚本执行过程中,通过系统监控工具获得系统性能的相关指标的值,包括系统资源利用率、响应时间、数据吞吐量。通过实时性能监测来确认性能指标或发现性能问题。例如,流行的负载测试工具 HP LoadRunner (LR) 有以下 4 个核心组件。
另外,作为性能测试工具,一般还会提供下列功能。
9.6 测试自动化的框架¶
自动化测试 (Test Automation, TA) 框架,不仅能够很好地支持脚本的开发、调试和运行,还要能够灵活地集成相应的测试工具,管理测试机器、测试任务和测试报告等,例如,之前介绍的 JUnit、Selenium/Watir-webDriver 以及 Appium/Calabash 也可以分别被看作是单元测试、Web 功能测试以及移动应用的自动化测试框架。优秀的 TA 框架能够与现有测试管理系统、源代码管理系统、软件包构建系统或文件管理系统等集成起来。例如,当一个新软件包被构建之后,能自动安装和配置到测试环境上。TA 框架由下列部分组成,如图 9-6 所示。

(1) Harness/IDE: TA 框架的核心,相当于 “夹具”, 其他 TA 框架的组成部分都能作为插件与之集成,而且承担脚本的创建、编辑、调试和管理等。
TA 执行的要求很清楚 —— 测试人员可以预先安排测试任务,在客户端以 Web 方式提交测试任务、查看测试结果。如下班前提交一个晚上 9 点运行的测试任务,到了晚上 9 点,系统会自动启动任务执行。不仅能安排和提交测试任务,还要能够管理测试任务、控制或操作测试工具的运行,所以 Harness 要有管理和控制能力。例如,开源 TA 框架 (Software Testing Automation Framework, STAF) 提供测试所需的基本服务,提供 TA 框架所需的底层通信,并使不同的测试工具进程之间交换数据。STAF 需要和软件自动化框架支持 (Software Automation Framework Support, SAFS) 及自动化框架执行引擎 (STAf eXecution engine, STAX) 结合起来使用。
再进一步,TA 框架能把 TA 运行的测试用例 / 任务和手工运行的测试用例 / 任务集中起来一起管理;TA 框架能够与持续集成环境、配置管理系统(如 SVN、Git 源代码管理)和缺陷管理系统等集成起来,TA 脚本直接由配置管理系统接管,持续构建后直接触发自动化测试,做到持续集成、持续测试,TA 所发现的缺陷能方便地被加入到缺陷库中,形成一个良好的开发和测试整合的环境。
对敏捷测试来说,自动化测试框架更为重要,一般会选择轻量型、开放类型的框架,其典型案例可以参考 RobotFramework (code.google.com/p/robotframework/), 它是基于 Python 开发的、可扩展的框架,适用于多种接口的复杂软件 (如用户接口、命令行、Web Service、编程接口等) 的测试。又比如 Thoughtworks Mingle+Cruise+Twist, 能帮助我们有效管理敏捷项目和协同工作,允许使用 BDD 开发模式和 Groovy 动态语言来编写测试脚本,包括手动和自动方式来创建可复用的自动化测试脚本,并结合测试领域特定语言 (Domain Specific Languages,DSL) 实现自动化测试。BDD 开发模式在敏捷测试中也很受关注,这类框架如 Cucumber、RSpec、NBehave/CBehave/JBehave、EasyB、JDave 等。
小结¶
测试工具的使用是自动化测试的主要特征,也是自动化测试的主要手段。自动化测试,有时不需要测试工具,而是使用一些命令、Shell 脚本就可以完成测试任务;其次,自动化测试不能仅局限于工具本身,必须和测试目标和测试策略结合起来,包括自动化测试的思想、流程和方法,在流程上支撑自动化测试的实现,在方法上保证选用正确的测试工具。
测试工具可以根据不同的测试方法、对象和目的进行分类,如白盒测试工具、黑盒测试工具、单元测试工具、功能测试工具、负载测试工具等。选择测试工具不仅要遵守一定的程序和步骤,而且要注重测试工具的特性,结合自己的实际应用特点,选择合适的工具。同时,在测试自动化实施时,不仅要了解普遍存在的问题,克服这些问题,建立合适的目标,做好人才和知识等各方面的储备,而且,要将自动化测试纳入整个软件开发流程之中,找准切入点,逐步推进自动化测试的工作,达到事先设定的目标。
本章着重介绍了功能测试工具和性能测试工具的一些关键特性,对选择合适的工具和充分使用好测试工具都有很大帮助。最后,还系统地介绍了自动化测试框架的概念和构成。
思考题¶
第 3 篇¶
软件测试项目实践¶
软件测试方法和技术最终要应用到实际工程项目中,通过项目实践来检验,也只有通过不断的项目实践来获取经验,真正提高自己的测试实战能力。从软件开发基本过程看,软件测试经历从需求评审、设计评审、单元测试到系统测试、验收测试等过程,而从软件项目看,软件测试经历从测试计划、测试设计、测试环境设置、测试执行、测试结果分析直到最终提交测试报告这样一个过程。而测试计划的基础是测试需求分析,而且在这个过程中还涉及测试用例的建立和维护、缺陷的报告和跟踪。在测试项目管理过程中,也需要做好资源、进度、风险和文档的管理,但这些知识和技能可以在 “软件项目管理” 课程中学习,只是要注意软件测试的特点,例如,测试总是有风险的,测试工作在后期容易受到需求变更的影响,从而更有效地管理软件测试项目。
本篇总共 5 章:
第 10 章 测试需求分析与测试计划
第 11 章 设计和维护测试用例
第 12 章 部署测试环境
第 13 章 测试执行、缺陷报告与跟踪
第 14 章 软件测试和质量分析报告