UML软件工程组织

使用 IBM Rational Functional Tester 实现自动化框架: 数据驱动
作者:Michael Kelly (Mike@MichaelDKelly.com), Consultant, www.MichaelDKelly.com 

这是应用 IBM Rational Functional Tester 实现测试自动化框架的三部系列的第二篇文章,重点在于创建一个数据驱动的框架。本系列的其他文章涵盖了模块化和关键字驱动的框架。
编者注: 本文是基于使用 IBM? Rational? Functional Tester for Java? and Web 6.1 和 Windows XP Professional SP2 编写的。代码实例将使用 Java 编写的,但所有概念同样也适用于 Rational Functional Tester 的 .NET 版本。

这是应用 IBM Rational Functional Tester 实现测试自动化框架的三部系列的第二篇文章。一个测试自动化框架是为软件自动化测试提供支持的一组假设、概念和实践。在本系列中所涵盖的三种框架类型包括模块化、数据驱动和关键字驱动。本文将特别侧重在创建一个数据驱动框架的方面。

数据驱动测试是一项单个测试脚本使用不同的输入和响应数据被重复地执行的技术,这些数据来源于一个预定义的数据集。在 Rational Functional Tester 中,数据驱动测试可以通过不同的数据池来实现,数据池是相关数据记录的集合,用以在测试脚本回放时为测试脚本提供变量的值。当您使用数据池时,Rational Functional Tester 能在您每次回放脚本时将一组不同的测试数据或相同的测试数据提交到测试脚本。您使用数据池来提供真实的数据,并用不同的数据和实际的数据量来产生对应用程序压力。当您运行一个数据驱动脚本时,Rational Functional Tester 将从数据池的当前行中输入每一个值(数据池中的一行看起来就象电子数据表中的一行)到应用程序的适当字段中。在每次执行脚本时,假定数据池光标在前面(数据池光标是对当前行的指针),下一个数据行将被输入。

在这个系列的三种框架中,数据驱动是最容易在 Rational Functional Tester 中实现的。下一部分我们将着眼于 Rational Functional Tester 中的数据池,帮您了解一下这个工具的内置特性。之后,文章将对这种方法进行利弊讨论。

在 Rational Functional Tester 中使用数据池进行工作

在 Rational Functional Tester 中,一个数据池是相关数据记录的一个集合,用以在测试脚本回放时为测试脚本提供变量的值。当您在 Rational Functional Tester 里创建一个数据驱动测试时,您可以用不同的输入和响应数据来重复地使用单个测试脚本。下面介绍的是一些在 Rational Functional Tester 里有关数据池的术语,以及如何创建和使用一个数据池。

Rational Functional Tester 通过引用数据驱动一个测试脚本来使用数据池 。当您在文档中看到这个术语时,它提到了构建您的测试脚本中的对象和您数据池中的数据之间的连通性的过程。要数据驱动一个脚本,您需要在被测试应用程序中选择一个对象,然后 Rational Functional Tester 将应用程序里的数据填充到一个数据池。

在 Rational Functional Tester 中使用数据池进行工作

让我们使用 Rational 的测试应用程序 ClassicsCD 来记录一个简单的数据池。

在 Rational Functional Tester 里开始记录一个新的测试脚本。
在如图1所示的 Select Script Assets 对话框中,您将在开始记录之前能够看到两个数据池选项:Test Datapool 和 Datapools Record Selection Order。

 图1. Select Script Assets 对话框
 
 Test Datapool 的默认值是 Private Test Datapool。您创建的每一个测试脚本都有一个私有的测试数据池与其相关联--这是自动生成的。初始的私有测试数据池是一个占位符,并且直到您给它增加新数据前都是空的。您可以通过创建一个新数据池,或者通过将几个测试脚本关联到一个数据池来创建一个被称为共享数据池的数据池。

Datapool Record Selection Order 框的值是 Sequential 和 Random。Sequential 指在回放时,测试脚本访问数据池中的记录是按照它们出现在数据池中的顺序来进行。Random 是指在回放时,测试脚本每次随机地访问数据池中的每条记录。

这此例中,接受缺省设置并点击 Finish。
 这样就应该打开了 Recording Monitor 窗口。
 启动 ClassicsCD (ClassicsJavaA.java) 样例应用程序,并导航到如图2所示的 Member Login 窗口。

图2. ClassicsCD Member Login 窗口
  
 在 Recording 工具栏上,点击 Insert Data Driven Commands。
测试脚本记录暂停下来,并且打开了如图3所示的 Insert Data Driven Actions 对话框。
在 Insert Data Driven Actions 对话框里有两个组合框:Populate then Select Test Objects 和 Data Driven Commands。

图3. Insert Data Driven Actions 对话框
 
 在 Populate then Select Test Objects下,有两种方法: Press and drag hand to select test objects 和 Use selection wizard to select test objects。Press and drag hand to select test objects 方法选择一个对象以及所选对象的所有下级对象。这是选择一个对象的最普通和直接的方法。Use selection wizard to select test objects 方法被用来激活 Drag Hand Selection 方法及其选项,或 Test Object Browser 方法。

使用鼠标将 Object Finder 工具拖拽到 Member Login 窗口中的Full Name 字段。
 Rational Functional Tester用一个红色边框画出对象的轮廓。
 释放鼠标按钮。
 您现在应当在如图4所示的 Insert Data Driven Actions 对话框的 Data Driven Commands 区域中,看到所列出的测试对象。

图4. 带有测试对象的 Insert Data Driven Actions 对话框
 
 点击 OK 关闭 Insert Data Driven Actions 对话框。
 点击 Member Login 窗口上的 Cancel 。
 退出 ClassicsCD 应用程序。
 停止记录。
 用数据池进行工作

如果您在刚刚记录的脚本里查看代码,您应当看到类似于列表1中的一行代码。

列表1. 在 Rational Functional Tester 中记录的数据池调用

// Data Driven Code inserted on Sep 14, 2005
nameCombo().select(dpString("nameCombo"));

在这段代码中,您选择与命名为nameCombo的数据池列中的值相匹配的组合框值。如果您在 Script Explorer 中打开 Private Test Datapool,您应当看到在第一行中所列出的您所记录的数据,如图5所示。

图5. 在 Private Test Datapool 中记录的数据
 
 在 Rational Functional Tester 中的一个数据池中,数据池列被称为变量,行被称为记录(不要问我为什么)。您只要在数据池表格(如图5所示)中右键点击,将显示一列数据池编辑命令,如图6所示。

图6. 在 Rational Functional Tester 里编辑一个数据池
 
 所有这些命令正确地执行了您所期望它们做的;不要惊讶。唯一不值得的事情是,当您声明一个新的变量(或者列)时,需要为变量指定 Type。当声明类型时,您将要输入类的完全路径。例如,使用java.lang.String,而不是简单地键入String。

数据池文字替换

Rational Functional Tester 提供了一种能力,可以用一个数据池引用(一个数据池引用是对有一个关联数据池的另一种说法)来查找或替换一个测试脚本中的文字值。在工具栏或者脚本菜单中,您可以访问 Datapool Literal Substitution 对话框,如图7所示。

图7. Datapool Literal Substitution 对话框
 
 您可以在 Datapool Literal Substitution 对话框中设置选项,用一个数据池引用来查找和替换一个脚本中的所有的、数字、字符串或布尔型文字。您也可以从一个脚本中增加一个文字到一个数据池中。如果您不使用一个已有的数据池变量,Functional Test 就在您每次运行脚本时,使用相同的文字值(Functional Test 在您记录测试脚本时捕获的值)。

数据池作为验证点

您可以使用一个数据池引用来替代您正在验证点中测试的值的一个文字值。在记录时,如果您使用 Verification Point and Action 向导插入一个证实点,您可以通过点击向导的工具栏中的 Convert Value to Datapool Reference 按钮,将验证点值转换为一个数据池引用。这个按钮用红色圆圈标记,如图8所示。

图8. 将验证点转换成数据池引用
 
 在您完成记录测试脚本之后,您也可以向数据池中添加数据。

导入和导出数据

如果您在 IBM? Rational? TestManager 中已经有数据池了,那就没问题了。您可以将它们导入到 Rational Functional Tester 中,同样您也可以使用电子数据表格应用程序创建的任何简单格式的已有.csv文件。反过来也一样。只要您将所有数据放入 Rational Functional Tester 中,您就可以将数据池导入到一个 .csv 文件中,用一个电子数据表格应用程序来编辑它。在 Rational Functional Tester 的帮助文件中有如何进行这样做的指导。

回页首

一个用 Rational Functional Tester 进行数据驱动测试的简单例子

好了,已经向您介绍了使用数据池的基础知识,现在您将看一个有关数据驱动测试更加详细的例子。这个例子将使用 www.Google.com作为测试。Google Web 搜索有一些您将要测试的内置特性。这些特性包括:计算器、货币兑换、电影、号码搜索,以及旅游信息(以命名包含在强大的小文本字段中的许多特性)。记录的脚本如列表2所示,启动浏览器访问 Google.com ,然后执行一系列搜索和验证点,以验证上述特性。

列表2. 记录和回放 Google Web 搜索脚本

package tests;

import resources.tests.google_search_recordHelper;
import com.rational.test.ft.*;
import com.rational.test.ft.object.interfaces.*;
import com.rational.test.ft.script.*;
import com.rational.test.ft.value.*;
import com.rational.test.ft.vp.*;

public class google_search_record extends google_search_recordHelper
{
public void testMain(Object[] args)
{
startApp("www.Google.com");

// Calculator
text_q2().click(atPoint(191,10));
browser_htmlBrowser(document_google2(),
DEFAULT_FLAGS).inputKeys("5{+}5");
button_googleSearchsubmit2().click();
calculator_gridVP().performTest(2.0, 20.0);
image_goToGoogleHome2().click();

// Currency Conversion
text_q2().click(atPoint(192,13));
browser_htmlBrowser(document_google2(),
DEFAULT_FLAGS).inputKeys("3.5 USD in GBP");
button_googleSearchsubmit2().click();
currency_conversion_gridVP().performTest(2.0, 20.0);
image_goToGoogleHome3().click();

// Movies
text_q2().click(atPoint(98,14));
browser_htmlBrowser(document_google2(),
DEFAULT_FLAGS).inputChars(
"charlie and the chocolate factory");
button_googleSearchsubmit2().click();
movies_gridVP().performTest(2.0, 20.0);
image_goToGoogleHome6().click();

// Search by Number
text_q2().click(atPoint(177,13));
browser_htmlBrowser(document_google2(),
DEFAULT_FLAGS).inputChars("patent 5123123");
button_googleSearchsubmit2().click();
search_by_number_gridVP().performTest(2.0, 20.0);
image_goToGoogleHome8().click();

// Travel Information
text_q2().click(atPoint(163,10));
browser_htmlBrowser(document_google2(),
DEFAULT_FLAGS).inputChars("United 134");
button_googleSearchsubmit2().click();
travel_info_gridVP().performTest(2.0, 20.0);
image_goToGoogleHome10().click();

// Close the browser
browser_htmlBrowser(document_google2(),MAY_EXIT).close();
}
}

在图2所示的脚本中,测试的每个特性都记录了在搜寻文本字段中进行一下点击、输入搜索标准、点击搜索按钮、验证结果,并在结果页的左上角点击 Google 图象以为下个特性测试重新设置浏览器。在这里有许多重复的代码。列表3显示了相同的测试脚本,只是脚本现在是数据驱动的。

列表3. 数据驱动的 Google Web 搜索脚本

package tests;

import resources.tests.google_datadrivenHelper;

import com.rational.test.ft.*;
import com.rational.test.ft.object.interfaces.*;
import com.rational.test.ft.script.*;
import com.rational.test.ft.value.*;
import com.rational.test.ft.vp.*;

public class google_datadriven extends google_datadrivenHelper
{
public void testMain(Object[] args)
{
startApp("www.Google.com");

// Run test for each value in datapool
for(int i = 0; i<5; i++)
{
// Search
text_q().click(atPoint(71,11));
browser_htmlBrowser(document_google(),
DEFAULT_FLAGS).inputChars(
dpString("searchCriteria"));
button_googleSearchsubmit().click();

// Results
searchResults_textVP().performTest(2.0, 20.0);
image_goToGoogleHome().click();

// Increment the datapool
dpNext();
}

// Exit
browser_htmlBrowser(document_google2(),MAY_EXIT).close();
}
}

通过使用一个for循环(您也可以使用一些其它类型的循环),并且通过增量的移动数据池里的光标,您可以避免多次重复相同的代码。您所做的全部就是在 inputChars 中为搜索字段增加一个数据池调用,并向您的验证点上增加一个数据池引用。在您想要增加另一个测试时,只要向您的数据池中增加另一行,并在循环中增加计数器的值。在图9中显示的数据池显示了每个测试的搜索标准和搜索结果。

图9. 数据驱动脚本的数据池值
 
 当您比较 Google 记录的测试与 Google 数据驱动的测试时,您将看到的最大区别是事实上什么已经被测试了的可见性。在记录和回放脚本中,您必须滚动代码以发现搜索标准。在数据驱动的测试中,您需要做的所有事情就是查看数据池中的 searchCriteria 变量(列)。此外,在记录和回放脚本中,您需要分别查找和打开每个验证点以查找搜索结果。在数据驱动的测试中,所有您需要做的就是查看数据池中的 searchResults 变量。忘记有更少的代码吧,了解数据驱动测试在测试什么将更容易。

回页首

选择使用的数据

数据选择是沿着数据驱动测试之路的第一步。您需要选择驱动您的应用程序运行的数据,或者表示将输入到您的应用程序中的数据,或者两个都选。本节将迅速查看一下选择数据进行测试的5 种不同方式:基于风险、基于需求、基于可用性、使用生产数据,或者随机使用产生的数据。

选择基于风险的数据。选择测试数据的头号标准应当是风险。当您识别风险时,您要考虑到什么会导致出错。您正在寻找可能会发生的事件,这些事件将减少您能够交付带有正确特性、所要求的质量级别和在预算之内的项目的可能性。有三种划分风险种类的方法:

  • 按照风险的影响 -- 如果风险突然出现,与计划在进度、工作量或成本上的偏离
  • 按照风险发生的可能性 -- 风险突然出现的可能性(通常用百分比表示)
  • 按照风险的暴露 -- 影响乘以发生的可能性
  • 选择基于需求的数据。您也可以选择将使您明确地测试一个需求或一组需求的数据。查找将使您在您的应用程序中运行特性集、性能和安全特性的数据:
  • 如果您的应用程序有不同的任务,执行每个任务您将需要什么数据?
  • 您想要在您的测试覆盖中包括什么特性,以及您要使用它们要求什么数据?

此外,考虑在被测试的元素上不同的目标部署环境将有什么冲突。您所选择的数据列表应当包括针对被测试应用和目标环境的数据。您需要测试什么数据:

  • 硬件设备
  • 设备驱动程序
  • 操作系统
  • 网络和通信软件
  • 第三方的基本软件组件(例如,e-mail 软件,互联网浏览器)
  • 与所有这些元素的可能组合相关联的各种配置和设置
  • 国际化
  • 选择基于可用性的数据。您可能想要选择那些易于可用的数据,这可以包括:

用一种易于访问的格式生产数据(在以后的章节里详细讨论)

  • 来自于过去迭代的数据
  • 您的项目的手工测试人员所使用的电子数据表格
  • 来自于您公司里的其他项目或团队的数据
  • 来自于一些数据产生源的数据(在最后的一章里讨论)。

这里的想法是,如果数据是容易访问的,也是易用和有意义的,在您的测试中包括这些数据,可以节省时间和金钱。我强调易用性和有意义的,是因为您需要记住一件重要的事情,您不应该只是因为数据已经在那了并且是可以使用的就选择它--因为它可能是坏数据。

使用生产数据。收集测试数据的另一个策略是使用生产数据。尽管您不应当单独地依赖这种类型的数据,它可以是自动化测试场景的最丰富的来源,因为数据是应用程序将面临的真实场景的代表,并且因为它很可能将提供大量的不同场景。在一个最近的项目中,我们大约每周从一张表格中读取一次生产数据,并且我们在没有付出多少努力的情况下得到了300到500个场景的奖赏。您可以直接将数据导入测试环境里,将它读入到数据文件中用于后面的处理,或者实时地读取它,并按照您所的需要的他们进行转换。生产数据也是并行测试的一个极好来源。如果您在正在开发的系统中使用生产数据,您就会迅速地知道是否被开发的系统能够象在生产环境一样工作。这项技术特别有助于发现使用浮点值、转换率,以及与数据类型相关长度的问题。

但是,使用生产数据有一些需要小心的地方。生产数据很可能不包含您想要测试的许多特殊情况, 并且这不是理想测试场景的一个替换。还有一些潜在的法律问题围绕着生产数据的使用。特别是如果您把一些您的测试外包给另一个公司,您将想要确保检查您的公司在生产数据使用上的公司政策;如果没有已有的正式政策,您需要询问您公司法律部门的某些人。即使如果您不能直接使用生产数据,您也可能有更改某些值(名字,社会安全号,等等)和使用其余数据的可能性。

使用随机数据生成。很多工具包括测试数据生成程序。随机数据生成程序在产生大量用户数据的过程中特别有帮助(这取决于您正在测试什么和您想在产生过程中花费多长时间)。例如,如果您需要对金融信息进行关于舍入错误的测试,您可以:

  • 随机生成大量数据
  • 通过您的应用程序运行数据
  • 通过运行 Windows 计算器(假定 Windows 的计算器没有舍入错误)来模拟相同的计算
  • 比较结果以确保他们匹配

如果您需要,例如,要生成一组500个不同的客户名称及地址,您可以有一个生成程序(TestManager,Excel,Google Sets,等等)来创建一个带有名称和地址的表。

您应当避免一次选择您所有的测试用例中的数据。相反,采取一种增量和迭代的使用测试数据的方法,将您的工作量集中在您认为最有可能产生的对于既定测试周期有用的评估信息上。这会帮助缓和在一组单个数据或者测试类型上付出太多时间的风险 -- 疏忽其它数据或测试任务 -- 并且最小化在某些今后可能并不受关注的测试想法的数据上花费工作量的风险。

回页首

数据驱动框架的利与弊

在本系列所涵盖的三类框架中(模块化、数据驱动, 以及关键字驱动),数据驱动框架对于非技术人员是最容易实现的,因为几乎所有的功能都被构建在 Rational Functional Tester 中,并且支持执行记录和回放。这对于非技术人员(对于我来说没有充分的理由推荐它)来说,不但是简单的,而且是强大的。它导致了半自动文档化测试用例,减少了创建大量测试用例所要求的代码数量,并且使您可以进行一些测试类型,否则您必须进行耗时或麻烦的手工测试工作。

数据驱动框架的主要优势是它减少了为很多类型的应用程序创建测试用例的成本。例如,在金融服务或者保险应用程序中数据驱动框架是非常有价值的。在您查看一千个变量中的一个变量是如何影响一笔复杂计算的最终金额的地方,对于金融服务或保险应用程序,很多次测试用例都只有细微的差别。对于一个数据驱动的工具,您将会很难找到一个更好的市场。另一方面,我曾经参与一个涉及化学制图和化学合成分析软件有关的项目。对于这个应用程序,测试用例非常特殊和集中(并且依靠于GUI),以至于数据驱动框架就没有意义了。

数据驱动框架最大的缺点之一是,它创建新的测试用例太容易了。因为测试用例易于创建,您能够简单地运行和维护测试,这有时会导致测试用例膨胀。这不一定是一件坏事 -- 这就是您为什么想要使用类似的工具的原因之一。如果您只是因为测试用例容易创建,而不投入许多时间和金钱来维护测试集,那就要小心了。如果只是为了看到发生什么而创建一个廉价的测试并运行一次,那可能是值得的,但是一旦您开始在您的应用程序的多个版本上开始维护测试,测试的成本就开始提高了。您想要确保维护成本的合理性是足够有力的。

下一步

考虑一下能在您的自动化测试中使用的一些数据驱动测试的方式。寻找一下您发现您自己一再调用相同代码的地方 -- 就有可能发现在这些地方使用数据池的机会。不要觉得好像如果您想要进行数据驱动测试,您就需要立刻转换您所有的脚本。您不用这样。只要创建所需要的数据池,并且随着时间的过去,您将会把您的测试自动化发展成一个框架,在框架中,您将数据池作为脚本生成的一个基本部分。在许多应用程序中,对已有的数据池有高级的重用。正如 DBA 花费时间组织它们的数据一样,您也要花费时间组织您的数据。


版权所有:UML软件工程组织