消除识别测试对象过程中的二义性情形

2010-01-28 作者:贺 顾一 来源:IBM

 
本文内容包括:
IBM® Rational® Functional Tester 为测试人员提供了一个测试对象识别框架以及一个简单的用户界面,以执行基于 GUI 的测试脚本。但是,在有些条件下,例如测试一个基于 Eclipse 的程序,您可能会发现测试的 GUI 中全是二义性情形的测试对象。这些对象拥有几乎一样的属性,只有轻微的差异。很难直接指定目标对象属性的模式以及识别对象。识别的结果可能包含有多个二义性的类似对象,从而导致识别失败。本文向您介绍了怎样消除这些二义性的情况,并给出了一些指导以执行灵活且轻松的维护性测试脚本。

前提条件

安装 IBM® Rational® Functional Tester V7 版本。

Rational Functional Tester 项目结构的概述

  1. 导入范例 Rational Functional Tester 项目(查看 下载)。您将会看到一个名为 BaseCase 的脚本以及一个名为 util 的文件夹。
  2. 通过选择 Window > Open Perspective > Java 来切换至 Java™ 视图。您将会看到以下的三个包:
    • (default package)
    • resources
    • util

BaseCase 脚本位于默认的包中,其中还有它的辅助类 BaseCaseHelper ,以及与 Resources 包中脚本有关的其他配置文件。这两个包在 Functional Test 视图下看不到。您创建了 util 包,以及包中的类。在接下来的章节中,您将会使用到这个框架(见于图 1)。

图 1. Rational Functional Tester 项目的结构
 Java Perspective 下的包

快速浏览一下 find() 方法

建议您在 getMappedTestObject() 方法不能使用时,使用 find() 方法来找到一个测试对象。列表 1 中的代码显示了 find() 方法的结构。

列表 1. 找到方法结构
 
// Finds all candidates that match given search criteria starting at this TestObject
public TestObject[] find(Subitem properties);

find() 方法通过将测试对象的特定属性,与给定的标准相比较以找到一系列的候选对象。您可以通过代表属性的名字/值,或者包含有一个或者多个匹配字符串来搜索一个测试对象。如果使用默认的 getMappedTestObject() 方法不能识别一个测试对象,那么您可以首先定位它的父类,然后通过访问父类的 find() 方法来找到目标测试对象。显然,find() 方法提供了可观的便利。脚本不再取决于对象映射。搜索候选测试对象的结果是可预测的,因此也是可靠的。您可以通过对象的属性值来找到一个测试对象,这些属性值可能是一个简单的文本。维护脚本起来会更加容易。

但是,如果 getMappedTestObject() 方法不可行的话,find() 方法不会包含所有的事例。不会总有明显系列的属性值会匹配。find() 方法取决于明显系列的属性值。因此,如果测试对象没有这样一系列的属性,那么这种方法就不会正常地发挥其功能。但是我们可以在返回的数组中一个个地试一下测试对象,这并不是一个聪明的办法。有时,甚至很难找到测试对象可识别的父类,所以搜索必须从最顶层开始,例如从窗口开始。这就会降低测试对象的可识别性。

导航基于路径的对象识别

考虑一下 find() 以及 getMappedTestObject() 方法的缺陷,您需要另外一个方法来解决 Rational Functional Tester 对象识别中的二义性情形。在本文中,我们介绍了基于路径导航方法,来找到目标测试对象。我们将会使用程序 ClassicsJavaA 作为一个范例。为了让事情变得更加简单,我们假设 Place Order 按钮就是要找的测试对象。

图 2. 范例程序的主窗口
控制 Main 窗口中的图片和按钮

在记录 Place Order 按钮的点击操作之后,您会得到图 3 中对象映射的以下层级结构。您可以看到 Place Order 按钮是框架的子部分。框架会作为主窗口来显示,而且它就是 Place Order 按钮的位置所在。当然,在本例中,您可以简单使用 find() 方法来定位按钮。但是,我们将会尝试另外一种方法来解决这个问题。

图 3. 对象映射的层级结构
对象映射内的按钮测试对象

对基于导航对象识别的介绍

有时测试人员会通过可见的线索来搜索测试的对象,例如关系中的相对位置。例如,您可能会通过它与文件夹封面图片的相对位置来找到 Place Order 按钮。与之类似,对于一个给定的测试对象,叫做锚对象,您可以使用 Rational Functional Tester API 来找到带有给定路径的任何对象。再次使用上面的例子,您可以通过找到对象附近的位置、文件夹的图片以及特定路径来找到 Place Order 按钮。这里所谓的术语“路径”是指在对象映射中将一个对象节点移动到另一个上时的逻辑距离。

通过对象映射来进行导航

Private Test Object Map for Script BaseCase 窗口中,您知道 Place Order 按钮是主框架的子类。显然,该对象映射并 没有 包含主框架中所有的构件。按照以下步骤来得到对象映射树的大致轮廓:

  1. 点击 Test Object 然后在主窗口的菜单上点击 Insert Object(s) 以打开向导对话框。
  2. 使用 Object Finder 来强调主框架,然后选中它。
  3. 选择 Include all available objects on this window 然后点击 Finish

如图 4 所示,现在您可以看到添加至对象映射的主框架的更多子节点。

重点:
尽管您选择了“include all”选项,该选项并没有 包含主框架的所有子节点。该对象映射只能帮助您知道 Place Order 按钮的位置。

图 4. 对象影响更加具体的层级结构
添加的测试对象拥有多个附属对象

您可以使用另外一种工具,来找到主框架的所有子节点。实际上,您刚刚已经使用了它。它就是 Insert Object(s) 向导对话框(图 4)。使用这个向导,您就可以看到所有的 可映射对象。按照以下的步骤来定位 Place Order 按钮。

  1. 在主窗口中选择 Test Object >Insert Object(s) 以重新打开向导对话框(图 5)。
  2. 对于 Selection Method,选择 Test Object Browser
  3. 找到 Place Order 按钮,根据上面生成的测试对象映射给出的层级关系中的暗示进行操作。
图 5. 测试对象浏览器
对象映射中按钮的确切位置

根据 Test Object Browser,您可以在测试对象映射中看到许多的对象。对象浏览器中的对象的顺序,与测试对象映射中的顺序不同。但是,测试对象映射仅仅给了您一点暗示,以帮助您在对象浏览器中找到当前的对象。通过再一次按照前面介绍的步骤进行操作,您可以轻松找到文件夹的图形节点。然后您可以找到从图形节点到按钮节点的确切路径(见于图 6)。

图 6. 从图形到 Place Order 按钮的路径
对象映射中从图片到 Place Order 的路径

如图 6 所示,从图形节点到按钮节点的路径如下所述。注意子类的索引在这里非常重要。

  1. 首先,将三个层次移动到父层次上。
  2. 然后移动主框架的 FIFTH

使用 API 来执行导航操作

现在您已经知道,怎样找到两个测试对象之间的路径。在本部分中,您将会学到怎样使用 API 来自动定位带有给定路径的目标测试对象。这项任务所需的 API 在列表 2 进行了总结。

列表 2. 定位测试对象所需的 API
 
				/**
* This method returns an array of references to the object's mappable children.
* The references to the objects should be released by calling he unregister method.
* If the object has no mappable children, a 0-length array will be returned.
**/
TestObject[] testObject.getMappableChildren();

/**
* This method returns a reference to the parent object that appears in the object map.
* The references to the objects should be released by calling he unregister method.
**/
TestObject testObject.getMappableParent();

/**
* This method returns the topmost mappable parent object that appears in the object map.
* The references to the objects should be released by calling he unregister method.
**/
TestObject testObject.getTopMappableParent();

至于简单性,我们并不使用 unregister 方法来注销迭代的测试对象。ObjectFinder 类是含有所有导航操作的辅助类。图 7 中的 UML 显示了这些类之间的关系。注意出于稳定性考虑,TestObjectNotLocatedException 必须源自于 RuntimeException

图 7. 类与 UML 之间的关系
类 ObjectFinder 是外观类

考虑一下路径范例,列表 3 向您展示了怎样书写代码以找到 Place Order 按钮:

列表 3. 找到 Place Order 按钮的对象
 
				// This method is used to find the button Place Order
public GuiTestObject PlaceOrder() throws TestObjectNotLocatedException{
	// The path we specified to find the button from the album image
	// First move up 3 times, then move down to the fifth child
	String pathString = "P3;C5";
	Path path = null;
	try{
		path = new Path(pathString);
	}catch(IllegalPathStringException illegalpath){
		logError(illegalpath.getMessage());
	}
	
	// Assume that the imageAlbum() is already found
	return new GuiTestObject(ObjectFinder.locateTestObject(imageAlbum(), path));
}

首先我们指定一个路径字符串,它描述了从图标到 Place Order 按钮的路径。然后我们使用 ObjectFinder 类来定位我们想要的目标测试对象。

更好测试对象识别的建议

我们已经看到了,怎样以各种方法来找到一个测试对象。我们可以使用默认的方法来找到测试对象。我们还可以使用 find() 方法来找到匹配属性/值的测试对象。而且我们可以定位带有给定对象及路径的测试对象。在本段中,我们将会讨论怎样选择正确的方法,以及怎样组织 Rational Functional Tester 对象的源代码。

选择合适的对象识别方法

显然,默认的方法拥有丰厚的工具支持。Rational Functional Tester 提供了强大的工具,例如 Test Object MapScript Assure ,可以轻松解决脚本开发的复杂性问题,并降低开发所需的时间。但是,有一些脚本开发员所抱怨的正是简单性。他们认为工具隐藏了一些他们想要控制的细节。find() 方法是灵活和简单的。它使脚本开发员能够更强地控制测试脚本。这就是为什么在脚本开发中它得到广泛的应用,以替换默认方法的原因。基于导航对象识别方法非常有效和简单。有一点非常重要:它几乎可以在任何场所使用。但是,使用 UI 结构还是十分敏感的。

我们将会向您展示怎样使用 find() 方法,以及替代性的基于导航方法,同时享受使用工具所带来的便捷之处。

脚本组织的建议

回忆一下 Rational Functional Tester 对象结构的第一部分。我们知道所有的对象搜索方法默认条件下都是在辅助类中执行。例如,搜索 Place Order 按钮的默认方法如列表 4 所示。

列表 4. 默认的 Place Order 按钮代码
 
public abstract class BaseCaseHelper extends RationalTestScript {
	/**
	* PlaceOrder: with default state.
	* .class : javax.swing.JButton
	* accessibleContext.accessibleName : Place Order
	* name : placeOrderButton2
	* .classIndex : 0
	*/
	protected GuiTestObject placeOrder() {
		return new GuiTestObject(getMappedTestObject("placeOrder"));
	}
	
	/**
	* Schubert4_14Jpg: with default state.
	* .class : javax.swing.JLabel
	* iconDescription : Schubert4_14.jpg
	* .classIndex : 2
	*/
	protected GuiTestObject schubert4_14Jpg() {
		return new GuiTestObject(
			getMappedTestObject("schubert4_14Jpg"));
	}
}



然后您创建一个辅助类,该类扩展了默认的辅助类并超越了默认的方法。它如列表 5 中的代码所示。

列表 5. 创建辅助类的代码
 
public class BaseCaseUserHelper extends BaseCaseHelper {
// This method is used to find the button Place Order
public GuiTestObject PlaceOrder() throws TestObjectNotLocatedException{
		// The path we specified to find the button from the album image
		// First move up 3 times, then move down to the fifth child
		String pathString = "P3;C5";
		Path path = null;
		try{
			path = new Path(pathString);
		}catch(IllegalPathStringException illegalpath){
			logError(illegalpath.getMessage());
		}
		
		// schubert4_14Jpg() is object for the cover image
		return new GuiTestObject(
			ObjectFinder.locateTestObject(schubert4_14Jpg(), path));
	}
}
			

最后,让脚本类扩展用户定义的类。通过这种方法,您不需要编辑任何已存在的对象搜索方法。您只是超越了它们,将已存在的对象搜索方法的最强部分结合起来。

下载

描述 名字 大小 下载方法
示例脚本
RBAT_Impl.zip
10KB

参考资料

学习 获得产品和技术 讨论

火龙果软件/UML软件工程组织致力于提高您的软件工程实践能力,我们不断地吸取业界的宝贵经验,向您提供经过数百家企业验证的有效的工程技术实践经验,同时关注最新的理论进展,帮助您“领跑您所在行业的软件世界”。
资源网站: UML软件工程组织