转自:http://www.ibm.com/developerworks/cn/rational/09/userationalfunctionaltestereffectively/index.html
IBM Rational Functional Tester 提供了一些特性,用户可以使用这些特性来有效测试 GUI 程序。IBM Quality Software Engineering(QSE)团队引入了一个层级结构框架,也就是所谓的 QSE 框架,以让维护变得更加容易。该框架使用 Rational Functional Tester 对象认知特性。本文向您展示了怎样扩展 QSE 框架,以及怎样使用 Rational Functional Tester 的特性来提高测试的生产效率。您还可以得到可以有效支持测试多语言程序的建议。
IBM® Rational® FunctionalTester 提供了自动化测试的特性。为了使用记录特性,您可以记录一次测试活动并将其保存为测试脚本。在此之后,不论何时您想要执行相同的测试时,都可以运行该测试脚本。
在您测试却想要输入数据时,您可以在记录期间使用测试数据汇特性。如果您使用了测试数据汇特性,那么您就可以在运行测试脚本之前更改输入数据了,而不需要重复记录该测试活动。您可以使用验证点,Rational Functional Tester 会使用它来比较实际值和预期值,然后生成一个带有详细信息的报告,信息会向您展示是否得到了预期值。但是,使用记录特性您也许会关注,当测试的程序被更改过时,您也许会需要重新记录测试活动。
为了降低操作的难度,IBM Quality Software Engineering(QSE)团队引入了一个层级结构的框架,也就是所谓的 QSE 框架。该 QSE 框架会将 GUI 对象与测试用例隔离开来。Rational Functional Tester 提供了 Test Object Map 特性,使用该特性可以获得 GUI 对象。通过使用 GUI 对象的方法可以执行对 GUI 的操作。QSE 框架由以下三层组成。
您可以使用 QSE 框架来提高维护性。如果想要得到关于 QSE 框架的更多信息,可以引用参考资源章节中的 An Object-Oriented framework for IBM Rational Functional Tester 项。
本文向您展示了通过扩展 QSE 框架和使用 Rational Functional Tester 特性(例如测试数据汇和验证点),怎样更加有效地自动化测试。本文还向您介绍了一些技巧,这些技巧是关于怎样测试支持多种语言的程序的。
考虑一下测试图 1 中所示的程序。
在这个 GUI 中,您可以选择 Actions > Create New Set 菜单项,如图 2 所示。通过 Test Object Map 特性可以获得这些对象。
对于 QSE 框架提供的 jar 文件,您可以定义如图 3 和列表 1 所示的方法,以得到 Test Object Map 特性获取的 GUI 对象。
package appobjects;
import resources.appobjects.MainGUIHelper;
import ibm.widgets.*;
import ibm.widgets.ancestors.*;
import ibm.widgets.swt.*;
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 MainGUI extends MainGUIHelper {
public ToggleWidget getActions() {
TestObject to = actions(ANY, NO_STATE);
return new ToggleWidget(to);
}
public ToggleWidget getActionsCreateNewSet() {
TestObject to = actionsCreateNewSet(ANY, NO_STATE);
return new ToggleWidget(to);
}
public ToggleWidget getConfigureGroupDiscoveryOptions() {
TestObject to = configureGroupDiscoveryOptions(ANY, NO_STATE);
return new ToggleWidget(to);
}
public ToggleWidget getCreateNewView() {
TestObject to = createNewView(ANY, NO_STATE);
return new ToggleWidget(to);
}
public SubitemWidget getGrdTree() {
TestObject to = grdTree(ANY, NO_STATE);
return new SubitemWidget(to);
}
public ToggleWidget getRunGroupDiscovery() {
TestObject to = runGroupDiscovery(ANY, NO_STATE);
return new ToggleWidget(to);
}
public ToggleWidget getRunTraceAnalyzer() {
TestObject to = runTraceAnalyzer(ANY, NO_STATE);
return new ToggleWidget(to);
}
// -----------------------------------------
public void testMain (Object[] args)
{
// Unit testing can go here
// (will be deleted next time ClasGenerator is run)
}
} |
列表 2 中的程序模拟点击 Actions 菜单,然后点击 Create New Set 子菜单。
MainGUI mainGui = new MainGUI(); mainGui.getActions().click(); mainGui.getActionsCreateNewSet().click(); |
正如本例所示,您可以通过使用 GUI 对象来轻松控制 GUI。但是,找到用于执行 GUI 操作的方法会很困难。图 4 显示了一个范例:当您右击树形结构的节点时,考虑一下这个例子。
在树形结构中,您不能将每一个节点当做一个对象。如图 5 所示,您需要得到树对象,然后使用方法中的一种以右击树的节点。
如果您不知道 GUI 对象是怎样执行 GUI 操作的(就像本例),那么您可以使用记录特性,然后引用生成的程序。在本例中,当您记录右击节点时,您可以得到如列表 3 所示的程序。
public void testMain(Object[] args)
{
// Frame. Database Relationship Analyzer
grdTree().click(RIGHT, atPath("SAMPLE->ORDER->TRACE_ANALYSIS"));
} |
在 tasks 文件夹中编写这种程序并不是十分有效。相反,在类中编写通用方法以模拟以上的操作就比较合适了,它继承了 appobjects 文件夹中的 GUI 对象类。这种类型的类并不需要当做一个 Rational Functional Tester 脚本来创建:将其当做一个 Java™类来创建已经足够了。创建一个文件夹,例如一个名为 apputils 的文件夹,以存储这种类型的类。列表 4 中的程序显示了右击树节点的通用方法。 MainGUIUtil 类继承了 MainGUI 类。 MainGUI 类包含了主窗口中的 GUI 对象,它位于 appobjects 文件夹之下。前面使用的 grdTree 方法,可以用于将树对象替换为 MainGUI 类中定义的书对象,在本例中是 getGrdTree 方法。
public class MainGUIUtil extends MainGUI {
public void rightClickView(String dbName, String setName, String viewName) {
String s = dbName + "->" + setName + "->" + viewName;
getGrdTree().click(RIGHT, atPath(s));
} |
通过使用这样的方法,tasks 文件夹中的程序可以和 GUI 操作一样得到直觉地实施。
对于 QSE 框架,输入数据的过程如下所示。
您可以使用 Test Datapool 特性,来有效地执行数据输入操作。例如,图 6 显示了一个带有输入区域的 GUI。考虑一下该 GUI 中的输入数据。
在打开该 GUI 之后,开始记录,然后通过选择 Insert Data Driven Commands 获取该窗口。在获取之后,暂停您的记录操作,输入假程序数据以激活 OK 按钮,重命名记录操作,点击 OK,然后停止记录。当您这样做时,会生成如图 7 和列表 5 所示的程序。如果您想要对由多个面板组成的向导应用该技术,那么就在每一个面板中插入数据驱使的命令,这样您就可以参数化所有的输入区域了。
图 7. 随着 Test Datapool 特性一起生成的程序
package recordingobjects;
import resources.recordingobjects.RunTraceAnalyzerGUIRecordingHelper;
import com.rational.test.ft.*;
import com.rational.test.ft.object.interfaces.*;
import com.rational.test.ft.object.interfaces.SAP.*;
import com.rational.test.ft.object.interfaces.WPF.*;
import com.rational.test.ft.object.interfaces.dojo.*;
import com.rational.test.ft.object.interfaces.siebel.*;
import com.rational.test.ft.object.interfaces.flex.*;
import com.rational.test.ft.script.*;
import com.rational.test.ft.value.*;
import com.rational.test.ft.vp.*;
public class RunTraceAnalyzerGUIRecording extends RunTraceAnalyzerGUIRecordingHelper
{
public void testMain(Object[] args)
{
// Data Driven Code inserted on 2009/02/28
//
schemaName().setText(dpString("SchemaName"));
tableName().setText(dpString("TableName"));
startDate().setText(dpString("StartDate"));
startTime().setText(dpString("StartTime"));
endDate().setText(dpString("EndDate"));
endTime().setText(dpString("EndTime"));
ok().click();
}
} |
如果您定义了一个包含该程序的方法,那么其他的程序就可以调用该方法了。当您调用该方法时,测试数据汇中定义的值会在合适的区域中得到设置。正如您在图 7 和列表 5 中看到的那样, dpString 方法用于得到来自测试数据汇的值。
如果您更改了方法,以通过方法参数传递输入值,那么该方法就可以得到通用的应用了。在本例中,如果您为开始时和结束时使用了测试数据汇值,并通过方法参数动态地设置其他的输入值,那么方法如列表 6 所定义的那样。
public void runTraceAnalyzer(String schema,
String table,
String startDate,
String endDate)
{
schemaName().setText(schema);
tableName().setText(table);
startDate().setText(startDate);
startTime().setText(dpString("StartTime"));
endDate().setText(endDate);
endTime().setText(dpString("EndTime"));
ok().click();
} |
如果有一些结果会显示在 GUI 中,那么通过使用验证点特性,就可以很容易地比较实际值和预期值了。
例如,如果您想要得到如图 8 所示的表格,那么就在记录开始之后,将表格当做验证点,然后停止记录。它会产生如图 9 和列表 7 所示的程序。
package verificationpoints;
import resources.verificationpoints.TestResult1Helper;
import com.rational.test.ft.*;
import com.rational.test.ft.object.interfaces.*;
import com.rational.test.ft.object.interfaces.SAP.*;
import com.rational.test.ft.object.interfaces.WPF.*;
import com.rational.test.ft.object.interfaces.dojo.*;
import com.rational.test.ft.object.interfaces.siebel.*;
import com.rational.test.ft.object.interfaces.flex.*;
import com.rational.test.ft.script.*;
import com.rational.test.ft.value.*;
import com.rational.test.ft.vp.*;
public class TestResult1 extends TestResult1Helper
{
public void testMain(Object[] args)
{
table().performTest(ResultTableVP());
}
} |
复制以上的程序以创建如列表 8 所示的方法。如果您调用了该方法,那么 Rational Functional Tester 会自动比较实际值和预期值。
public void verifyResult() { table().performTest(ResultTableVP()); } |
如果您想要动态地更改预期值,那么您可以使用 IFtVerificationPointvpManual(java.lang.String vpName, java.lang.Object expected, java.lang.Object actual).performTest method。vpManual 方法有三个 参数。第一个参数是设置项目中独一无二的验证点名,第二个参数是设置预期的对象,而第三个参数是设置一个实际对象。如果您想要比较如本例中那样表格中的值,那么您可以使用vpManual方法中第二个和第三个参数的 com.rational.test.ft.vp.impl.TestDataTable类对象。您可以创建如列表 9 所示的预期对象。您还可以指定比较的区域,它是在 setComparisonRegions方法中完成的。
TestDataTable tbl = new TestDataTable(); tbl.setColumnHeader(0, "Table Name"); ... tbl.insert(new String[7], 0); tbl.setCell(0, 1, "CUSTINFO"); ... TestDataTableRegions regions = new TestDataTableRegions(); regions.addRegion(TestDataTableRegion.allCells()); tbl.setComparisonRegions(regions.getRegions()); |
在接下来的例子中,您可以使用验证点对象作为预期对象,它是通过获取 GUI 表格作为上例中的验证点得以创建的。它将会创建一个预期的对象。接下来,编辑该对象的值。为了得到实际值,您可以使用测试对象映射获取的table对象的getTestData(String testDataType)方法,如列表 10 所示。 testDataType 参数拥有显示在验证点窗口中的 type属性。您可以在图 10 中看到它。在本例中,该值是“可视的内容”。
Public void verifyResult() {
TestDataTable tbl = (TestDataTable)ResultTableVP().getBaselineData();
tbl.setCell(0, 1, "CUSTHIST2");
vpManual("VP1", tbl, table().getTestData("visible contents")).performTest();
} |
在本程序中,“CUSTHIST2”在一个单元中得到设置,以前它是“CUSTHIST”。运行这种方法会产生一个错误,您会得到如图 10 所示的信息。
前面的范例讨论了 table 对象。可以比较的其他对象如表格 1 所示。
| GUI 类型 | 类 |
|---|---|
| 树 | com.rational.test.ft.vp.impl.TestDataTree |
| 列表 | com.rational.test.ft.vp.impl.TestDataList |
| 菜单栏 | com.rational.test.ft.vp.impl.TestDataTree |
| Character 字符串 | 字符串 |
在 tree 对象中,列表 11 显示了一个添加节点到作为验证点获取的 tree 对象的程序。
public void verifyResult() {
TestDataTree tree = (TestDataTree)GRDTreeVP().getBaselineData();
TestDataTreeNodes nodes = (TestDataTreeNodes)tree.getTreeNodes();
TestDataTreeNode node = (TestDataTreeNode)nodes.getRootNodes()[0];
node = (TestDataTreeNode)node.getChild(0);
node = (TestDataTreeNode)node.getChild(0);
node = (TestDataTreeNode)node.getChild(0);
TestDataTreeNode node2 = new TestDataTreeNode();
node2.setNode("GROUP2");
TestDataTreeNode[] n = new TestDataTreeNode[2];
n[0] = (TestDataTreeNode)node.getChild(0);
n[1] = node2;
node.setChildren(n);
vpManual("TreeVP1", tree, grdTree().getTestData("tree")).performTest();
} |
当您运行该程序时,您会得到如下的信息(图 11)。
在 list 中,列表 12 显示了向作为验证点获取 list 对象添加项的程序。
public void verifyResult() {
TestDataTree tree = (TestDataTree)GRDTreeVP().getBaselineData();
TestDataTreeNodes nodes = (TestDataTreeNodes)tree.getTreeNodes();
TestDataTreeNode node = (TestDataTreeNode)nodes.getRootNodes()[0];
node = (TestDataTreeNode)node.getChild(0);
node = (TestDataTreeNode)node.getChild(0);
node = (TestDataTreeNode)node.getChild(0);
TestDataTreeNode node2 = new TestDataTreeNode();
node2.setNode("GROUP2");
TestDataTreeNode[] n = new TestDataTreeNode[2];
n[0] = (TestDataTreeNode)node.getChild(0);
n[1] = node2;
node.setChildren(n);
vpManual("TreeVP1", tree, grdTree().getTestData("tree")).performTest();
} |
当您运行该程序时,会得到以下的信息(图 12)。

为了测试多个语言的程序,您可以使用包含密钥和值对( [KEY]=[VALUE]),此处该值会显示在 GUI 中。资源文件的一个范例是在 Java 的 ResourceBundle 类中使用的资源文件。配置该文件的步骤如下所示。
# When enabled this property allows string look up in a localized string table, if available rational.test.ft.services.enable_localization=true |
例如,将英文资源文件复制到日本资源文件中,然后在每一个值的前面添加一些日本字符。您可以在如图 13 中的 GUI 中这样做。
打开测试对象图,然后将作为 GUI 显示值的属性值更改为资源文件的密钥名字,如图 14 所示。
当您按住 Enter 键时,密钥名就会更改为合适资源文件中的实际值,如图 15 所示。密钥名就存储在里面,值会根据语言环境而发生动态的变化。当您双击属性值,以再次切换编辑模式时,您可以看到资源文件的密钥名。当您更改资源文件的一些值时,然后重启 Rational Functional Tester,在测试对象图中您可以看到更改的值。
接下来的 name 属性,具有与上级菜单名相联系的值,由下划线(“_”)隔开。所以它的权重会更改为 0(图 16)。
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/17117101/viewspace-618397/ ,如需转载,请注明出处,否则将追究法律责任。