在诸如 IBM Workplace 的企业环境内执行模拟测试会是一项很重要的任务。本文将介绍如何用 Java 创建封装器类,这些类将调用封装到 Rational Functional Tester API 中,以模拟常见的用户活动。
Rational GUI 自动化工具有很好的记录器功能,可以记录用户的活动,并自动生成代码来模拟这些活动。然后,无需进一步修改,就可以立即回放这些代码来执行简单模拟测试。不过,诸如 IBM WebSphere Portal、IBM Workplace Collaboration Services 或 IBM Workplace Client Technology 之类的大型复杂应用程序通常需要更复杂的测试,这不是未经修改的记录和回放可以提供的。对于我们自己的 IBM Workplace 产品的内部测试,我们使用 Rational Functional Tester(以前称为 Rational XDE Tester)为模拟测试手工编写代码。在这个过程中,我们已经掌握了多种方法,这些方法使得为这些产品编写代码变得更容易、更快。
Rational Functional Tester 以 Java 为基础,因此可以非常容易地扩展其内置功能。这允许我们用 Java 创建封装器类,这些类通过将调用封装到 Rational Functional Tester API 中来执行针对控件的公共操作。在手工编写和维护用于测试产品的自动化代码时,这些类极大地提高了我们的生产率。去年,这些类已经传播到 IBM 的其他许多软件团队中。
在本文中,我们将说明如何创建这些封装器类,我们称这些类为窗口小部件 类。本文假设您有用 Java 编写测试脚本的经验。我们将带领您编写窗口小部件的代码,用这些代码测试文本字段、列表框和其他标准控件;搜索动态生成的控件;以及扩展这些类来处理定制 Java 控件。如果计划测试复杂应用程序,比如与 WebSphere Portal、IBM Workplace 或基于 Eclipse 的应用程序集成,以便使用 Workplace Client Technology 的那些应用程序,那么您将发现这些类非常有用,希望本文会激发您创建一些自己的类。
文本字段很好地说明了手工对 Rational Functional Tester 测试脚本编码时遇到的困难。在记录设置文本字段的文本的那些操作时,会得到类似下面的代码:
Text_name().click(atPoint(35,10)); Browser_htmlBrowser(document_C(),DEFAULT_FLAGS).inputKeys ("{ExtHome}+{ExtEnd}{ExtDelete}"); Browser_htmlBrowser(document_C(),DEFAULT_FLAGS).inputKeys("test"); |
该代码有些冗长。它使用了三行代码来完成一项任务:即设置文本。但是,在记录器中,这是可以理解的行为。因为记录器不知道您要设置文本(比如说,在没有单击文本字段或者没有先删除字段中数据的情况下输入一些文本),所以记录器不能用一个方法调用替代所有的三行。
不过,如果手工编写代码,而不是记录代码,那么在所有这些代码行中输入信息变得比较麻烦。对于手工编码,实际需要的是 setText() 方法,以便可以只输入一行代码,而不是三行代码都输入。要完成这项操作,可以将记录器生成的三行代码封装到测试脚本的某个单独方法中。
作为首要方法,可以创建类似下面的 setText() 方法:
void setText(String s) { Text_name().click(); Browser_htmlBrowser().inputKeys("{ExtHome}+{ExtEnd}{ExtDelete}"); Browser_htmlBrowser().inputKeys(s); } |
当然,这样做还不够,因为它将只为对象映射中的一个特定文本字段工作,即 Text_name。最好为所有文本字段生成此代码。进行这项操作的一种方法是将对象作为参数传递给方法,如下所示:
void setText(String s, GuiTestObject gto) { gto.click(); Browser_htmlBrowser().inputKeys("{ExtHome}+{ExtEnd}{ExtDelete}"); Browser_htmlBrowser().inputKeys(s); } |
如果您所担心的只是当前处理的测试脚本,那么该代码就可以解除您的顾虑。但是从长远角度来看,您实际想要的是一种可以从编写的所有测试脚本进行访问的方法。支持这一点的一种方式是将该方法放入 Super Helper,然后让所有测试脚本继承 Super Helper。(有关 Super Helper 的详细信息,请参阅 developerWorks:Rational 中的“Creating a super helper class in Rational XDE Tester”一文。)为所有不同种类的控件(文本字段、列表框等)创建所需的方法后,会产生巨大的、难处理的 Super Helper。更好的方法是创建单独的 Java 类,与每种控件相对应,然后将用来操作每个控件的方法放入相应的类中。
为了创建 TextField 窗口小部件类,可以通过在 Rational Functional Tester 开发环境中调用向导,在 Eclipse 中创建一个新的 Java 类。
创建新的 Java 类后,可以将下列修改的 setText() 方法放入该类中:
public class TextField { public static void setText(String s, GuiTestObject gto) { gto.click(); TopLevelTestObject app = (TopLevelTestObject) gto.getTopParent(); app.inputKeys("{ExtHome}+{ExtEnd}{ExtDelete}"); app.inputKeys(s); app.unregister(); }} |
现在可以从测试脚本中用简单的一行代码调用该方法:
TextField.setText("test", Text_name());
正如您可以看到的,手工编码要比通过记录器创建代码的方式进入每个操作容易得多。
此时,您可能希望向 TextField 类添加其他方法。至少应该向该类添加另一个方法,以获取文本字段的文本,如下所示:
public static String getText(GuiTestObject gto) {
return gto.getProperty(".value").toString();
}
用这种方式可以非常容易地封装从控件获取信息的方法。记住那些直接从 GuiTestObject 获取信息的所需的属性值非常困难,但是记住类似 getText() 的方法则非常容易,所以您将发现,封装与此类似的 getProperty() 调用将非常有用。
不过,您可能并不想封装 Rational Functional Tester 为操作文本字段提供的每一个 方法。如果某些人因为某些原因想跨文本字段实现拖动操作,或在这些字段上盘旋,您当然想也让他们这样做,但是为 GuiTestObject 中的每个方法编写封装器方法会很枯燥乏味,每个方法都要调用 Rational Functional Tester 已经提供的相应方法。相反,可以将 Rational Functional Tester 的 GuiTestObject 继承到您的类中。然后该类的用户可以使用 GuiTestObject 中的现有方法,您不必重新编写所有方法。
然而,如果继承 GuiTestObject,则需要编写构造函数。完成该操作后,将现有方法更改为不再保持静态。可以将 GuiTestObject 只传递到构造函数中一次,然后将设置和获取文本的方法用作实例方法,而不是用作类方法。(另外,如果有人扩展类,因为无法正确覆盖这些方法,静态方法可能会引发问题。)这使调用该方法变得不太直观。因此,不使用以下代码:
TextField.setText("test", Text_name());
而必须使用:
TextBox tb = new TextBox(Text_name());
tb.setText("test");
或者如果这个特定文本字段在脚本中只使用一次,那么可以将这个代码合并到一个语句中:
new TextBox(Text_name()).setText("test");
虽然这也不太直观(特别是对那些不熟悉 Java 的人),但它极大地改善了整个设计。
继承 GuiTestObject 后,最终的 TextField 类与下面的代码类似:
public class TextField extends GuiTestObject { public TextField(GuiTestObject textfield) { super(textfield); } public void setText(String s) { this.click(); TopLevelTestObject app = (TopLevelTestObject) gto.getTopParent(); app.inputKeys("{ExtHome}+{ExtEnd}{ExtDelete}"); app.inputKeys(s); app.unregister(); } public String getText() { return this.getProperty(".value").toString(); }} |
在 IBM 使用的 TextField 类中,有其他一些方法,其中包括清除文本的方法,以及输入 escape 键的方法,这两种方法都通过 setText() 方法使用。在本文中,我们使用简单的代码来表达这种策略的要点。您可以根据需要添加其他方法。
|
在 Rational Functional Tester 中,对列表框进行手工编码要比文本字段难得多。经常需要对列表框执行无数操作,比如按名称或整数选择某些项、获取所选项的名称或索引、获取可选择的数目,以及获取所有选择名称的列表。但是这些操作需要在 Rational Functional Tester 中输入多行难记的代码。
例如,每次在列表框中选择项时,您都会发现,输入代码
List_category().click(); //click once to expose the drop down list
List_category().click(atText("Computer Related")); //then select the text
要比输入下列代码麻烦得多:
new ListBox(List_category()).select("Computer Related"));
另外,如果想使用 Rational Functional Tester 的 API 选择某个项,则需要确保选择这个项之前存在该项,因此必须进行类似下面的操作:
ITestDataList dataList = (ITestDataList)this.getTestData("list"); ITestDataElementList elementList = (ITestDataElementList)dataList.getElements(); for(int i = 0; i<elementList.getLength(); i++) { if (elementList.getElement(i).getElement().toString(). equals("Computer Related")) { List_category().click(); List_category().click(atText("Computer Related")); } } |
使用窗口小部件类要容易得多,可以将这项功能封装到易于记住的方法中,如下所示:
ListBox lb = new ListBox(List_category()); if (lb.doesItemExist("Computer Related") { lb.select("Computer Related")); } |
要完成这项操作,需要创建 ListBox 窗口小部件类,创建过程与前面创建 TextField 的相似,然后将 select() 和 find() 方法添加到该类中。select() 方法很简单,但是实现 find() 方法时,该方法要使用我们在该类中已经公布的其他方法。我们向您显示了完整的 ListBox 窗口小部件类(当然,为了便于显示,这些类已经清除了),这使您可以了解我们已经发现的对操作列表框有用的许多方法。正如您将看到的,这类方法有很多,因此,ListBox 窗口小部件类显示了从创建窗口小部件类中可以获得的一些实际的好处。要查看 ListBox 窗口小部件类,请参阅 this sidefile。
|
您可能认为没有太大必要为文本字段和列表框之外的控件创建窗口小部件类。诸如链接、按钮、复选框和单选钮之类的其他通用部件的确不是很难处理。大多数时间,使用 click() 就足以解决问题。除了要与已经创建的窗口小部件一致之外,为什么还要为其他控件创建单独的类呢?
答案涉及两个方面。首先,经常需要从特定控件获取属性。正如前面所讨论的,调用 getText() 方法要比调用 getProperty(".value") 直观得多。除了文本字段和列表框之外,最明显地体现了这一点的控件是复选框和单选钮。
例如,您不仅经常要选择复选框,而且通常还想知道该复选框是否选中。我们在 CheckBox 窗口小部件类中提供了 isChecked() 方法来执行这项操作。我们发现记住如何编写下列代码:
new CheckBox(CheckBox_1()).isChecked();
要比记住如何编写以下代码容易得多:
CheckBox_1().getState().isSelected();
而且,该代码允许创建 check() 和 uncheck() 方法,这些方法可以在执行操作前查询对象的状态。同样地,每次选中复选框时,我们发现,记住如何编写下列代码:
new CheckBox(CheckBox_1()).check();
要比记住如何编写以下代码容易得多:
CheckBox_1().clickToState(State.selected());
其次,更重要的是,我们发现经常需要动态查找特定类型的控件,因为这些控件是在运行时生成的,或者因为每次构建的时候,它们的位置会发生变化。这项功能常常用于链接、静态文本、图形和按钮,所以我们在这些窗口小部件中添加了特殊方法来动态查找它们,如下例所示,该例根据链接的文本属性来查找链接:
public static Link findHtmlLinkFromTextProp(Regex linkText, TestObject parent) { TestObject to = ObjectFactory.findTestObject(linkText, ".text", "Html.A", parent); //Make sure the object exists if (to == null) throw new ObjectNotFoundException("Link with text '" + linkText + "' not found"); return new Link(to); } |
该方法在名为 ObjectFactory 的类中调用了另一个方法,以完成所有动态搜索。我们发现动态搜索非常有用,所以值得详细研究一下 ObjectFactory 类。
|
在一些 Web 应用程序中,经常在运行时动态生成控件。例如,IBM Workplace Messaging 等标准基于 Web 电子邮件应用程序在 Inbox 中以链接形式列出消息。
每个进入 Inbox 的新消息都将创建一个新的动态生成的链接。对于 Rational Functional Tester 的对象模型,这样做会存在问题。如果按图 1 中显示的方式映射链接,那么再次运新测试时,随着其他邮件发送到 Inbox,链接的顺序可能会变化。随着顺序变化,链接的重新配置属性也将变化,这可能会导致自动化失败。因此,您所需的是一种在运行时识别链接的方法,换句话说,需要一种动态查找链接的方法。这就是 ObjectFactory 类所要做的。
理解这个问题的最简单的方法是考虑这样一种情况:要在动态 Web 应用程序中为链接随机生成惟一名称。在 WebSphere Portal 自动化中,我们将进行这项操作。创建新的 Collaborative Space 或 Place 时,WebSphere Portal 提供下列 Web 页面表单:
需要为运行的每个测试创建新的位置,所以需要输入随机生成的名称来惟一标识每个位置。单击 Creat 按钮后,会生成新的、惟一 命名的位置,该位置的链接现在显示在位置列表中,如图 3 所示。
不过,如果不能动态访问这个新生成的、惟一命名的链接,就不能继续自动测试。因为运行之前 不知道位置的名称,所以记录测试情况时,不能将得到的链接添加到对象映射。相反,需要在运行时动态访问链接。
ObjectFactory 类用动态对象识别方法解决了这个问题。这些方法帮助自动化开发人员轻松地找到动态生成的 GUI 对象,并对其进行操作,这些 GUI 对象是在运行时生成的。通过在 ObjectFactory 类中使用 findTestObject() 方法,自动化开发人员可以利用以下调用轻松访问以前动态创建的位置链接:
ObjectFactory.findTestObject("AAAAPlace_721134315", ".text", "Html.A", Browser_Page());
该方法将搜索所有页面内容,查找包含文本 AAAAPlace_721134315 的 Link 类对象,然后返回要执行操作的对象。
在 ObjectFactory 类中,findTestObject() 方法调用名为 findTestObjectRF() 的递归方法,下列代码中显示了该方法。该方法接受 4 个参数:属性、属性值、类和父测试对象。利用这些信息,该方法可以在父 TestObject 的派生项中搜索包含给定属性值的对象。
public TestObject findTestObjectRF(String sProperty, String sValue, String sClassID, TestObject t) { //check if object was found. If yes return it. if (gbKill) return t; else gbKill = false; //declare array of TestObjects TestObject[] objList; //get top level children if (sClassID.equals(gsTextRef)) { objList = t.getChildren(); } else { objList = t.getMappableChildren(); } //Find dynamic web object for (int x = 0; x < objList.length; x++) { if (gbKill) return t; //try to find specified object try { //check for specified class if (objList[x].getProperty(gsClassProp).toString() != null) { //check for specified property if (objList[x].getProperty(gsClassProp).toString() .indexOf(sClassID) != -1) { //check for specified property value if (objList[x].getProperty(sProperty) .toString().indexOf(sValue) != -1) { gbKill = true; //if test object is found set to true gtoTestObj = objList[x]; //set global test object holder to appropriate test object return gtoTestObj; //return found test object } } } } catch (Exception e) { //continue; } //get child objects if (sClassID.equals(gsTextRef)) { if (objList[x].getChildren().length > 0) { findTestObjectRF(sProperty, sValue, sClassID, objList[x] ); } } else { if (objList[x].getMappableChildren().length > 0) { findTestObjectRF(sProperty, sValue, sClassID, objList[x] ); } } } //end loop return gtoTestObj; } |
该方法在给定父测试对象(通常是浏览器页面)内搜索指定类的对象,例如,Link、TextField、Listbox 等。找到指定类的对象后,它检查该对象的指定属性值是否等于搜索值。如果属性值与搜索值匹配,那么该方法就成功地找到预期的测试对象,并返回该对象。这样,可以在应用程序内动态搜索具有任何给定属性值的任何对象。
注意:在本文发表时,Rational Functional Tester 6.1 的新发行版本将动态搜索功能内置到了名为 find() 的 TestObject 方法中。获得新版本后,我们将调用这个 find() 方法,替代前面介绍的递归函数,您也应该如此。这里需要重点了解的是 find() 方法如何有用。
动态搜索功能非常有用的另一种情况是:随着每次进行新的构建,应用程序都会发生很大的变化。我们常常发现,在测试中,每次构建新的应用程序,对象识别属性的更改都很大,以致于破坏了自动化。进行每日构建意味着需要频繁更新对象映射。限制经常管理和更新对象映射的一个方法是使用 ObjectFactory 类的动态对象搜索功能。我们不记录(映射)经常变化的应用程序中的所有对象,而是仅映射最主要的对象,并使用 findTestObject() 动态搜索方法查找其他对象。
因为任何类型的控件都存在这个问题,而不仅仅是链接存在这个问题,所以我们给所有窗口小部件都添加了构造函数,该构造函数可以动态查找控件,然后构造窗口小部件。例如,TextField 窗口小部件具有如下所示的构造函数:
public TextField(String sTextField, String sProperty, String sClass, TestObject parent) { super(ObjectFactory.findTestObject(sTextField, sProperty, sClass, parent)); } |
其他所有窗口小部件中都有相似的构造函数,以便动态查找和构造任何类型的窗口小部件。
最后要提到的是我们创建了 StaticText 窗口小部件类。在 Rational Functional Tester 中,不能映射 HTML 静态文本。不过,查找 Web 页面上的静态文本通常非常有用,例如,用来验证 HTML 表或文档中显示的文本。因此,我们创建了可以动态构建的 StaticText 窗口小部件,还有静动态 find() 方法,这些方法与为 Link 窗口小部件创建的那些方法类似。
正如您可以看到的,内置到 ObjectFactory 类中的动态搜索功能对操作运行时生成的对象非常有用,同时,该功能在为经常变化的 GUI 对象映射提供稳定性方面也非常有用。我们已经将主要动态搜索方法的其他许多变体构建到了 ObjectFactory 类中,其中包括基于正则表达式进行搜索的变体、搜索具有特定属性的控件的第 n 次出现的变体,等等。我们在窗口小部件类中调用了其中多种方法,使窗口小部件类功能更加强大。
|
到目前为止,我们主要从 HTML 测试角度讲述了窗口小部件类。然而,只要对这些窗口小部件类进行很少的更改,就可以将它们用于测试 Java 应用程序。为 Java 应用程序修改这些类时,需要更改使用 getProperty() 从这些方法中查询到的值,因为这些值经常不同,甚至对于相同类型的控件也是如此。还有其他一些小的差别,但是都很容易通过编码解决。
为了使窗口小部件类适用于测试 HTML 和 Java 应用程序,我们在 ObjectFactory 类中创建了下列方法:
public static boolean isHTML(TestObject to) { String sClass = to.getObjectClassName(); return sClass.indexOf("Html.") != -1; } |
当某一个方法必须调用不同的 Rational Functional Tester API 代码在 Java 和 HTML 上执行同一操作时,可以使用这个方法测试当前处理的对象是 Java,还是 HTML,然后根据答案调用适当的代码。这样,我们可以使用窗口小部件类测试 Java 或 HTML 应用程序。
您可能发现您所拥有的 Java 控件可以扩展本文前面所述标准控件功能。在出现这种情况时,可以创建新的窗口小部件类,该类继承控件所基于的标准窗口小部件的功能,然后添加新的控件所需的额外功能。例如,在 Java 中,有一些合并了列表框和文本字段功能的实际的组合框。为处理这些组合框,我们创建了 ComboBox 窗口小部件类,该类将继承 ListBox 并添加 setText() 方法。
此外,您还可能发现应用程序在使用一些定制控件,这些控件提供更广泛的功能。也就是说,开发人员可能在内部创建原始控件。有时可以使用 Rational Functional Tester's GuiTestObject 提供的基本方法(例如,click()、getChildren() 等)操作这类控件。在这种情况下,可以创建封装器方法来调用这些 GuiTestObject 方法。
然而,为了有效地操作定制控件,常常需要获得 Java 控件的内容信息,通过该控件的公共方法就可以访问这些信息。幸运的是,Rational Functional Tester 提供了接口,通过该接口可以调用某个 Java 控件的任何公共方法,这个控件就是 TestObject 的 invoke() 方法。遇到这种情况时,可以将定制控件封装到其自己的窗口小部件类中,根据需要继承其他标准窗口小部件,然后将 invoke() 调用封装到该类的方法中。
为了使用简单的例子进行说明,我们的开发人员创建了 Java applet,向用户提供了一个富文本字段。在该字段中,用户不仅可以输入文本,而且还可以使文本显示为粗体、斜体或为文本加上下划线,并使用不同的颜色和字体,等等。该控件如下所示:
测试此控件时,我们不但希望使控件内的文本成为斜体或粗体(这可以通过单击一个按钮来完成),而且还希望查询控件当前是否设为使文本成为斜体、粗体或其他任何形式。要实现这项操作,需要访问该控件内部的 Java 方法。我们请开发人员将需要调用的方法变为公共性质,以便公开这些方法,然后我们可以使用 Rational Functional Tester 的 invoke() 方法来访问它们,如下所示:
public boolean isBold() { return ((Boolean)getBoldButton().invoke("getSelected")) .booleanValue(); } |
当然,这个类中还有许多其他方法,它们对控件内的公共方法使用相似的调用,对于应用程序中的其他所有 Java applet 控件,都有匹配的窗口小部件类。此外,这个简单的例子显示了如何将 invoke() 调用封装到窗口小部件类的方法内,使脚本代码更易于编写和维护。
我们的一个同事使用这项技术创建了用于测试 SWT (Standard Widget Toolkit 控件的窗口小部件类,这些控件是 IBM 创建的一组特殊的 Java GUI 控件,应用于 Eclipse 应用程序中。通过使用将 invoke() 调用封装到窗口小部件类的方法中这项技术,可以更容易地对这些 SWT 控件的自动化操作进行编码。因为 IBM 对 SWT 控件的使用非常广泛,所以这些窗口小部件使我们受益匪浅。
|
对比拥有窗口小部件和没有窗口小部件的 Rational Functional Tester 代码
一切都已就绪,现在,我们可以通过一个简单的例子,说明使用窗口小部件类可以获得的效率。假设您想对在下列 IBM Workplace 页面中创建 Team Space 的操作进行自动化,那么需要执行以下步骤:
要使用 Rational Functional Tester 的记录机制,请执行下列步骤:
然后需要添加多行代码,进行正确的测试验证和结果记录。完成上述操作后,Rational Functional Tester 记录的代码将如下所示:
browser_htmlBrowser(document_ibmWorkplace(),DEFAULT_FLAGS) .click(atPoint(452,14));button_newButton().click();text_cdoName().click(atPoint(85,10));browser_htmlBrowser(document_ibmWorkplace2(),DEFAULT_FLAGS) .inputChars("TestSpace1");text_cdoName_textVP().performTest();logInfo("Enter text: TestSpace1");list_templates().click(atText("Discussion"));list_templatesVP().performTest();logInfo("Selected Discussion Template");text_nDescription().click(atPoint(33,13));browser_htmlBrowser(document_ibmWorkplace2(),DEFAULT_FLAGS).inputChars ("This is a test space");text_nDescription_textVP().performTest();logInfo("Enter text: This is a test space into the Description text area");button_oKbutton().click(); |
另一方面,要使用窗口小部件封装器类生成相同的测试自动化,只需输入下列五行代码即可:
new Button(button_newButton()).click();new TextField(text_cdoName()).setText("TestSpace1");new ListBox(list_templates()).select("Discussion");new TextField(text_nDescription()).setText("This is a test space");new Button(button_oKbutton()).click(); |
后面的代码示例使用了窗口小部件封装器类,看上去要直观得多,并且生成相同测试案例所需的编码要少得多。在 IBM,我们已经将验证和记录直接构建到窗口小部件类中,所以在使用我们的窗口小部件类版本时,不需要那些额外的代码。
当然,这是非常简单的例子,仅涉及一个非常简单的测试案例。请想像一下,如果正在编写数百个测试案例,用它们来测试整个 IBM Workplace 特征区域,或者编写数千个测试案例来测试整个产品系列,将会获得怎样的生产效率和可维护性!
|
通过将复杂的 Rational Functional Tester API 代码封装到窗口小部件类的方法中,我们使脚本的手工编码更加容易。对于文本字段和列表框,这些益处是显而易见的。即使对于其他控件,也有一些虽然轻微但很重要的益处。首先,它使获取控件的属性变得容易得多。其次,它可以用简单的方法动态构造对象,对于测试动态 Web 应用程序(比如 IBM Workplace Collaboration Services),以及对于确保随着属性发生变化,自动化代码不会因为每次的构建操作而遭到破坏,这种构造方式可以使它们从中受益。第三,它提供了基础设施,基于该基础设施可以扩展窗口小部件类来包括定制控件。
因为所有这些原因,我们的窗口小部件类在 IBM 中非常流行。10 多个国家/地区的不同团队中的成百上千的人都在使用这些类。它们已经极大地提高了快速创建更有效的自动测试的能力。如果您像我们一样发现某些应用程序需要大量手工编码,那么我们建议您创建自己的窗口小部件类集合,以简化编写的代码。
Tim Snow 领导 Notes 客户机的 Automation Services 团队。自从 2003 年,他已经率先转换到 Rational Functional Tester。Tim 具有丰富的知识背景,从 Java 和 C++ 编程,到设计专家系统,再到分析哲学的博士学位。 |
Tony Venditti 是 IBM Workplace 的 GUI 自动化架构师。自从 1989 年以来,Tony 一直在从事软件自动化工作和开发工具,工作过的公司有 Lotus Development Corporation、Iris Associates、Fidelity Investments、Avid Technology 和 IBM。最近,Tony 率先开始在 IBM 内实现和使用 Rational Functional Tester。 |