第 5 章 软件测试工具 本章将对多种测试工具进行详细介绍,并通过具体实例对BoundsChecker、 JUnit、LoadRunner、Monkey等测试工具,以及测试管理工具禅道进行演示。 ..5.1白盒测试工具BoundsChecker BoundsChecker是一种常用的动态白盒测试工具,其常用于单元测试中的 代码错误检查,使用简单且高效。下面将对其进行详细介绍,帮助读者更快地 了解以及学会使用该工具。 5.1 安装 1. BoundsChecker是集成在VisualC+ + 上的一个插件,因此在安装 BoundsChecker之前,首先要确保计算机中已经安装了VisualC++。在获得 BoundsChecker安装程序后,以管理员的身份运行setup.exe文件,按照提示进 行安装即可。安装成功以后,可在VisualC++环境下看到名为BoundsChecker 的菜单,如图5. 1所示。 图5.r在V+环境下的菜单 1 BoundsCheckeisualC+ 134软件测试技术与研究 5.1.2功能与模式 BoundsChecker是一个功能强大、使用方便的代码检查工具,在程序运行期间,它可 以跟踪以下3种程序错误。 (1)指针操作和内存、资源泄露错误。例如,对指针变量的错误操作、内存泄漏等 问题。 (2)内存的错误操作。例如,未初始化内存空间即使用,内存的读写溢出等错误。 (3)API函数使用错误。 BoundsChecker提供两种模式给用户进行错误检测:一种为ActiveCheck;另一种为 FinalCheck。与FinalCheck模式相比,ActiveCheck检测的错误类型有限,一般为内存泄 漏错误、资源泄露错误、API函数使用错误。而FinalCheck则包含了BoundsChecker可 以检测的所有的错误类型,除ActiveCheck可以检测的错误类型之外,还可以对指针错误 操作、内存溢出等内存操作错误进行检测,提供更详细的错误信息,其是ActiveCheck的 超集。 5.1.3ActiveCheck模式 使用ActiveCheck模式检查的步骤如下。 (1)在VisualC++环境下打开要测试的程序,并使程序处于Debug状态下。 (2)选择BoundsChecker菜单中的IntegratedDebugging与ReportErorsand Events命令,确保BoundsChecke如图5. r可以发挥作用,2所示。 (3)选择VisualC+ + Build菜单的StartDebug→Go命令,使程序在Debug状态下 运行,ActiveCheck也将在后台下启动并进行错误检测。当程序运行结束后, BoundsChecker将给出错误报告。 y命令,3所示, (4)如果选择ReportErorsImmediatel如图5.则当检测到错误发生 时会立即停止运行,并弹出错误提示框,4所示。 如图5. 图5.r菜单选项(一) 图5.r菜单选项(二) 2 BoundsChecke3 BoundsChecke 单击第1个按钮表示暂时忽略这个错误,继续运行程序。 单击第2个按钮BoundsChecker会跳转到程序出现问题的代码处。待处理完问题, 可选择Debug→go命令继续运行。 单击第3个按钮表示将该错误添加至可忽略列表,对以后出现的此错误不予报告。 单击第4个按钮表示终止程序的运行。 单击第5个按钮会显示当前的内存使用情况。 第5章软件测试工具135 图5.4错误提示框 单击第6个按钮会显示当前错误的有关帮助信息。 单击第7个按钮即代表选择ReportErrorsImmediately命令。 单击第8个按钮代表选择ReportErrorsandEvents命令。 单击第9个按钮会显示或者隐藏与错误相关的函数调用堆栈情况。 5.1.4FinalCheck模式 如需在FinalCheck模式下测试程序,则不能使用VisualC++集成开发环境提供的 编译连接器来构造程序,而必须要使用BoundsChecker提供的编译连接器。当 BoundsChecker的编译连接器编译连接程序时,会向程序中插桩一些错误检测代码,因此 FinalCheck能够比ActiveCheck找到更多的错误。 使用FinalCheck模式进行错误检查的步骤如下。 (1)在VisualC++环境下打开要测试的程序。 (2)选择Build菜单的Configurations命令。 (3)在弹出的对话框中单击Add按钮,在Configuration文本框中添加需要创建的文 件夹名称。 (4)在Copysetingsfrom组合框中选择×××-Win32Debug选项,单击OK按钮, 然后再单击Close按钮即可。 (5)选择Build菜单的SetActiveConfiguration命令,选中步骤(3)中新建的文件夹, 单击OK按钮,这样BoundsChecker的编译连接器编译连接程序时生成的中间文件、可 执行程序都会被放到该文件夹下。 (6)选择BoundsChecker菜单的RebuildAlwithBoundsChecker命令,对程序重新 进行编译连接。在这个过程中,BoundsChecker将会向被测程序的代码中加入错误检 测码。 (7)选择BoundsChecker菜单中的IntegratedDebugging与ReportErorsand Events命令。 (8)运行VisualC++环境中Build菜单的StartDebug→Go命令,程序将在Debug 状态下运行。 两种模式都可以对程序进行检测,但正如5.2节所述,Acieek所能检测的错 1.tvChc 136软件测试技术与研究 误类型有限,而FinalCheck模式下可以检测BoundsChecker所能支持检测的所有错误类 型,与此同时,FinalCheck需要付出运行速度变慢的代价。 5.1.5结果分析 在程序结束以后,BoundsChecker会给出一份错误列表,其将包含程序中出现的内存 泄漏、指针操作错误、API 函数使用错误以及资源泄漏等错误,如图5.5所示。测试人员 需要根据错误列表进行分析,找到错误原因以及位置。 从图5.5中可以看出,左边的窗格中逐条给出了程序在内存、资源、API 函数使用上 的问题,包括问题的类型、出现次数、具体问题描述等。在单击某条问题时,右边窗格会显 示与该问题相关的函数调用堆栈情况,双击某条问题时,BoundsChecker会定位到引起该 问题的源代码处。 图5.错误列表 5 虽然BoundsChecker给出了较为详尽的错误报告,但测试人员仍然需要发挥主观能 动性进行分析。因为工具的使用只能帮助测试人员更快捷方便地检测,但无法保证其结 果是完全正确的。在使用中BoundsChecker会存在误报的情况,其可能有以下两种情 况:第一种是由于工具算法问题将正常的代码检测为错误;第二种是由于 BoundsChecker指出的问题存在于第三方代码中,如第三方的程序库等。对于错误列表 中的错误,测试人员应该进行仔细分析,在确认非程序出错时可将错误设为忽略,以防下 次再提醒。 ..5.单元测试工具JUnt 2i 5.2.1 JUnit简介 JUnit是一个可编写重复测试的简单框架,是基于XUnit架构的单元测试框架实例。 它由KentBeck和ErichGamma 建立,并逐渐成为源于KentBeck,SUnit的XUnit家族 中最为成功的一个。 JUnit测试主要用于程序员测试,即白盒测试,因为程序员知道被测试的软件如何完 成功能和完成什么样的功能。JUnit是一套框架,其继承TestCase类,现在大多数Java 开发环境都已经集成了JUnit作为自带单元测试的工具。 第5章 软件测试工具1 37 5.2.2 JUnit的优势与核心功能 1.JUnit的优势 (1)简化测试代码的编写,每个单元测试用例相对独立并由JUnit启动,自动调用, 不需要添加额外的调用语句。 (2)可以书写一系列的测试方法,对项目所有的接口或者方法进行单元测试。 (3)添加、删除、屏蔽某条测试方法时不影响其他的测试方法,几乎所有开源框架都 对JUnit有相应的支持。 (4)能使测试单元保持持久性。 2.JUnit的核心功能 (1)测试用例(TestCase):创建和执行测试用例。 (2)断言(Assert):自动校检测试结果。 (3)测试结果(TestResult):测试的执行结果。 (4)测试运行器(Runner):组织和执行测试。 5.2.3 根据血糖判断健康状况 本案例主要根据空腹血糖以及餐后两小时血糖联合进行健康判断,判断标准如表2.1。 1.创建测试类 首先创建被测类,部分核心代码如下: public class BloodSugar { private double empty; private double later; public double getEmpty() { return empty; } public void setEmpty(double empty) { this.empty = empty; } public double getLater() { return later; } public void setLater(double later) { this.later = later; 1 38 软件测试技术与研究 } public void setParams(double e,double l){ this.empty=e; this.later=l; } public BloodSugar(double e,double l){ this.empty=e; this.later=l; } }p ublic String GetBloodSugarType() { String result = ""; if (empty >7. 0 & later >11. 1) { result = "糖尿病"; } else if (empty <6. 1 & later <7. 8) { result = "正常血糖"; }else if (empty < 7. 0 & later <=11. 1 & later >=7. 8) { result = "糖耐量减低"; } else if (empty >=6. 1 & empty <= 7. 0 & later <7. 8) { result = "空腹血糖受损"; } else { result ="血糖值错误"; } return result; } 编写完成后可以写几个简单的测试用例测试一下。 public static void main(String[] args){ BloodSugar test=new BloodSugar(7.5,12); System.out.println(test.GetBloodSugarType()); test.setParams(5,6); System.out.println(test.GetBloodSugarType()); test.setParams(6,8); System.out.println(test.GetBloodSugarType()); test.setParams(6.5,7.5); System.out.println(test.GetBloodSugarType()); test.setParams(1,200); System.out.println(test.GetBloodSugarType()); } 第5章软件测试工具139 单击运行后结果如图5.6所示。 图5.6测试结果 可以看到,程序的功能已经基本实现,可以根据输入的空腹血糖值和餐后两小时血糖 值判断人的健康情况。 在被测类代码处右击,在弹出的快捷菜单中选择GoTo→Test命令,如图5.7所示, 创建测试脚本。 图5.创建测试脚本 7 继续选择CreateNewTest命令, 如图5. 新建脚本,8所示。 图5.新建脚本 8 1 40 软件测试技术与研究 选用JUnit4测试库进行测试,如图5.9所示,创建JUnit测试类。 图5.9 创建JUnit测试类 选中需要测试的类和方法后,即可自动生成JUnit测试类,部分代码如下所示: @Test public void getBloodSugarType() { } @Test public void main() { } 2.编写测试方法 使用JUnit生成测试脚本,并编写部分代码如下: public class BloodSugarTest { BloodSugar testObj; @Before public void setUp() throws Exception { testObj=new BloodSugar(); System.out.println("Run @Before method"); } 第5章 软件测试工具1 41 @After public void tearDown() throws Exception { testObj = null; System.out.println("Run @After method"); } @BeforeClass public static void prepareEnvironment(){ System.out.println("Run @BeforeClass method"); } @AfterClass public static void RestoreEnvironment(){ System.out.println("Run @AfterClass method"); } } 测试用例的执行顺序如下。 (1)@Before:在每个测试用例执行之前执行一次。 (2)@After:在每个测试用例执行之后执行一次。 (3)@BeforeClass:在测试类的所有测试用例执行之前执行一次。 (4)@AfterClass:在测试类的所有测试用例执行之后执行一次。 继续编写测试用例,部分代码如下: @Test public void getBloodSugarType() { System.out.println("Run getBloodSugarType"); testObj.setParams(6,7); String actual=testObj.GetBloodSugarType(); String expect="正常血糖"; assertTrue(expect==actual); } 其中,GetBloodSugarType的作用即调用被测方法。 assertTrue即断言,其可以判断预期结果expect和运行结果actual的差别。 @Category({EPTest.class}) @Test public void getBloodSugarType_1(){ System.out.println("Run getBloodSugarType1"); testObj.setParams(6,8); assertTrue(testObj.GetBloodSugarType()=="糖耐量减低"); 1 42 软件测试技术与研究 } @Category({EPTest.class}) @Test public void getBloodSugarType_2(){ System.out.println("Run getBloodSugarType2"); testObj.setParams(6.5,7); assertTrue(testObj.GetBloodSugarType()=="空腹血糖受损"); } @Category({EPTest.class}) @Test public void getBloodSugarType_3(){ System.out.println("Run getBloodSugarType3"); testObj.setParams(7,11.1); assertTrue(testObj.GetBloodSugarType()=="糖尿病"); } 这里用了@Category 注解,JUnit提供了一种分类运行器,其包含在org.junit. experimental.categories.Category中,基本步骤分成两步。 (1)创建新的测试类,并配置该测试类。 (2)修改已有测试类,定义具有特定分类的方法。 采用这种分类器可以将测试用例分类,以满足不同的测试类型。 在应用到测试用例之前首先完成第一步,创建新的测试类,其代码如下。 @RunWith(Categories.class) @Categories.IncludeCategory({EPTest.class}) @Suite.SuiteClasses({BloodSugarTest.class}) public class CategoryTest { } @RunWith注解指定运行器;IncludeCategory设置要执行的测试特性为EPTest,测 试特性也可被理解为专有一类测试的标签;SuiteClasses 设置候选测试集为 BloodSugarTest。 第二步修改已有测试类,在@Category注解后加上{EPTest.class}以表明该测试用 例的标签。 注意在使用EPTest标签之前必须要先定义EPTest接口类,其代码如下。 public interface EPTest { } 之后该代码即可执行,执行结果如图5.10所示。