UML软件工程组织

 

 

使用 IBM Rational Functional Tester 6.1 进行功能测试: 第三部分: 功能回归测试、测试框架和测试调试
 
出处:IBM Rational
 
本文内容包括:
本文将帮助你利用之前章节学习的知识,开始实践现实测试工作中可能遇到的测试场景 - 功能回归测试。我们将记录三个功能测试脚本并把它们放在一起合并为一个回归测试的过程。

功能回归测试

我们将记录三个功能测试脚本并用它们组成回归测试。脚本将是非常基本的,建立时间不会长,但是在建立它们的过程中我们将回顾Functional Tester的一些关键特性。我们将使用BookPool.com作为我们的应用程序测试对象。请记住该站点的内容会发生变化,这将会影响本文中包含的例子的运行结果。我们选用一个实际存在的网站作为测试目标的目的是让大家不只是在试验环境下体验Functional Tester 的功能,在真实的环境下进行实践才能真正验证大家是否真的掌握之前学习的基本知识。

术语回顾

功能测试的准确定义是什么?功能测试是一个帮助你决定系统功能是否符合预期设计的测试。使用功能测试你可以测试操作和图形用户界面对象的外观。比如,一个命令按钮的功能测试既包括了对按下按钮时动作的测试,也包括了对按钮的大小,位置和颜色的测试。

在自动化的环境中,回归测试指的就是运行以前开发的测试脚本,把最新编译的被测试应用程序与你早先建立的基线进行比较。回归测试揭示了从上次编译以来被引入到应用程序的不同之处。由此你可以评估这些差异来决定它们实际是缺陷还是经过考虑的变化。

测试的选择

自动化测试的第一步是选择你的测试。为回归测试选择哪些测试需要自动化既是一种科学又是一种艺术,而且为了有效地进行选择,花费时间学习一些策略是很重要的。你可以遵从一些简单的指导原则并使用一些常识,但是通常你还需要一定的直觉,这意味着你做得越多你得到的也越多。在本课程当中我们没有介绍选择哪些测试进行自动化,哪些测试应为手工测试的细节,但是我建议大家去学习一些关于测试策略方面的知识。

下面我们开始第一次在真实的环境下,利用你所学到的知识进行回归测试。

创建功能测试项目

在你可以记录脚本之前,你需要一个功能测试项目。按照下列步骤建立功能测试项目:

1. 打开IBM Rational Functional Tester。

2. 如果工作空间向导打开了,点击OK选择默认的工作空间。

3. 一旦Functional Tester打开完毕,确保已经通过点击屏幕右上角的Functional Test按钮选择了 Functional Test视图。

4. 从菜单中选择文件 > 新建 > Functional Test 项目。

5. 在"建立一个新的功能测试项目"对话框中,输入项目的名称(在我们的例子中,使用名字BookPool)并点击完成。



图1 "建立一个新的功能测试项目"对话框
图1 建立一个新的功能测试项目对话框
 

这样就在功能测试项目视图中加载了项目。

创建一个功能测试脚本

现在我们将进入为了回归测试的目的而记录三个功能测试的步骤。脚本将是很基本的,建立不需要很长时间,但是在建立它们的过程中你将回顾一些之前你学习过的Functional Tester的关键特性。

在你开始记录你的回放脚本以前,你将需要为它们在项目中建立一个文件夹。这将帮助你避免混淆,在有很多脚本的情况下井井有条,而且在后面你需要一次执行所有的回放脚本时它还将对你有所帮助。

1. 建立文件夹的方法是,右键点击功能测试项目视图中的BookPool项目,从弹出的菜单中选择添加测试文件夹。



图2 从菜单中选择添加测试文件夹
图2 从菜单中选择添加测试文件夹
 

2. 输入文件夹的名称(在我们的例子中,使用RegressionScripts)并点击完成。



图3 创建测试文件夹对话框
图3 创建测试文件夹对话框
 

你应该在功能测试项目视图中看到文件夹被列在BookPool项目下。

测试一:检验客户是否可以搜索

首先,我将告诉你如何创建一个检验网站是否包含允许客户进行搜索的功能的测试脚本。对这个测试来说,你只需要简单地打开网站,在搜索域中输入文本,点击搜索,然后检验是否出现了正确的结果总数以及前三个搜索结果是否是你期待的书名。

搜索

1. 在功能测试项目视图中右键点击RegressionScripts文件夹,在弹出的菜单中选择使用记录器添加脚本。



图4 功能测试项目视图
图4 功能测试项目视图
 

2. 这将打开"记录一个新的功能测试脚本"向导。为脚本命名(在我们的例子中,使用VerifySearch)然后点击完成。



图5 "记录新的Functional Test脚本"向导
图5 记录新的Functional Test脚本向导
 

3. 这将打开功能测试记录监视器。从这个监视器你可以启动你想要测试的应用程序,记录程序内的动作,记录检验结果的验证点,并在记录的同时向脚本中加入数据驱动元素。在这个测试中,你只需要把重点放在基本步骤上--包括一些简单验证点的添加。



图6 功能测试记录监视器
图6 功能测试记录监视器
 

4. 打开浏览器访问站点www.BookPool.com。点击启动应用程序图标来打开"启动应用程序"向导。默认情况下不会列出任何应用程序,因此你需要把BookPool网站加入应用程序列表。



图7 "启动应用程序"向导
图7启动应用程序向导
 

5. 点击编辑应用程序列表按钮。这打开了应用程序配置工具。



图8 应用程序配置工具
图8 应用程序配置工具
 

6. 点击右边的添加按钮。这打开了"添加应用程序"向导要求你选择你添加的应用程序的类型的部分。



图9 在"添加应用程序"向导中选择应用程序类型
图9 在添加应用程序向导中选择应用程序类型
 

7. 点击HTML应用程序单选按钮然后点击下一步。添加应用程序向导要求你选择一个HTML应用程序。



图10 在"添加应用程序"向导中输入URL
图10 在添加应用程序向导中输入URL
 

8. 为你想要测试的应用程序输入URL(在我们的例子中是BookPool.com)并点击完成。这使你回到应用程序配置工具,此时www.BookPool.com应用程序应出现在应用程序列表中。



图11 应用程序配置工具
图11 应用程序配置工具
 

9. 点击完成。现在你应该可以在"启动应用程序"向导的应用程序名的下拉菜单中看到你的应用程序了。



图12 "启动应用程序"向导
图12 启动应用程序向导
 

10. 选择应用程序并点击确定。这启动了带有已加载BookPool.com网页的浏览器。当Functional Tester 在后台工作时,这一启动可能需要几秒钟。

11. 点击搜索域并输入"Software Testing"作为搜索文本。



图13 在搜索域中输入搜索文本
图13 在搜索域中输入搜索文本
 

12. 点击Search按钮。结果页面应被加载并与下图近似。注意你的结果依据你记录测试的时间不同将有所变化。



图14 在 BookPool.com上搜索"Software Testing"的结果
图14 在 BookPool.com上搜索Software Testing的结果
 

创建验证点

首先,为了验证所有搜索到的结果都已被返回,你将建立一个HTML表单的验证点,它包含全部 53条结果。 1. 在功能测试记录监视器中点击"插入验证点或动作命令图标"。这将打开"验证点和动作"向导。



图15 "验证点和动作"向导
图15 验证点和动作向导
 

2. 点击对象查找器图标并将其拖至浏览器中的"Software Testing"的全部53个结果 窗体中。



图16 用红线圈住的浏览器中的"'软件测试'的全部53个结果 "表单
图16 用红线圈住的浏览器中的软件测试的全部53个结果表单
 

3. 你一旦选择了表单,"验证点和动作"向导就会再次出现并要求你选择对窗体进行的动作。点击"执行数据验证点"单选按钮并点击下一步。



图17 选择一个动作标签,并选上"执行数据验证点"
图17 选择一个动作标签,并选上执行数据验证点
 

4. 下一个画面允许你对验证点的一些属性进行修改。把数据值改为可见文本,因为你必须看着文本进行检验。如果愿意的话你还可以更改验证点的名称,但是默认的名称一般是比较有描述性的,因此我认为使用默认的就很好。最后,选中"包含重试参数 "复选框,这样Functional Tester将会每两秒钟检查一次你的应用程序中验证点的存在,并持续二十秒(这是默认值;你也可以输入你自己需要的值)。在测试Web应用程序时这是非常方便的,因为你并不知道下载网页和显示信息需要多少时间。



图18 插入检验点数据命令标签,选中了可见文本
图18 插入检验点数据命令标签,选中了可见文本
 

5. 点击下一步。这打开了"验证点和动作"向导的最后一个画面--你选择包含在你的验证点内的属性的页面。这个页面体现了Functional Tester更强大的特性,但并不像它看上去那么复杂。



图19 验证点数据标签,选中
图19 验证点数据标签,选中



 

即使你不会对这一页面做出任何更改,你也应该注意它的内容。测试对象面板和识别标签对任何类型的验证点都会出现。而右面的面板依据你在向导的前几个画面选择的不同将会显示不同信息。

  • 测试对象面板包括一棵树,它是脚本对象图的一个部分版本。这一分级显示只包含你的验证点内的对象。你不能对树进行编辑,但是你可以选择其中的对象并在右边面板的属性列表中编辑它的属性。通过检查测试对象面板中的节点你就把那个节点中的数据包括进了你的验证点。在上面的例子中,你测试的只是你选择的表格,不是那个表格的任何父母。
  • 识别标签显示了Functional Tester用来定位和管理测试对象的识别数据。管理标签显示了对象的内部管理数据。这些标签都不能编辑。元数据标签是你可以编辑的,它显示了定义特定数据如何管理的一组属性。
  • 右边的面板显示了对象属性。由于你建立了一个数据验证点并选择了可见文本测试,显示的文本是一种文本框的形式。在这个文本框中,你可以编辑数据,把数据转换为正规表达式,并/或为数据驱动测试把数据转化为数据库引用。

1. 点击完成接受默认设置。

2. 现在你要为搜索到的前三本书分别添加一个验证点来保证搜索结果的顺序是正确的。再次点击功能测试记录监视器中的"插入验证点或动作命令"图标。

3. 使用对象查找器,选择列表中的第一本书--或者更精确地说,搜索结果表格中的第一个单元。在我们的例子中,那本书是Testing Computer Software ,作者是Cem Kaner et al 。



图20 用红线圈起来的搜索结果表格中的第一本书
图20 用红线圈起来的搜索结果表格中的第一本书
 

4. 一旦你选中了书,在"验证点和动作"向导中点击"执行数据验证点"单选按钮,和前面的步骤三是相同的,然后点击下一步。

5. 在下一个页面中,把数据值改为可见文本并选中"包括重试参数"复选框,和前面的步骤四相同。点击 下一步然后点击完成。

6. 对搜索结果中列出的后两本书重复这个过程。

完成并运行这个脚本

现在你将要完成并运行脚本以确认它是正确的。

1. 关闭浏览器。

2. 点击功能测试记录监视器上的停止记录图标。

Functional Tester此时应该产生一个与列表1近似的测试脚本。



列表1 刚刚记录的VerifySearch回放脚本
 
package RegressionScripts;
import resources.RegressionScripts.VerifySearchHelper;
public class VerifySearch extends VerifySearchHelper
{
public void testMain(Object[] args) 
   {
// HTML Browser
startApp("www.BookPool.com");
// Document: Bookpool: Discount Computer Books. Welcome!:
http://www.bookpool.com/.x/nqbosf60z8/ho
text_qs().click(atPoint(72,6));
browser_htmlBrowser(document_bookpoolDiscountCompu(),DEFAULT_FLAGS).inputChars
("Software Testing");
button_search_btnGif().click();
All53ResultsForSoftwareTestVP().performTest(2.0, 20.0);
SearchResults_KanerVP().performTest(2.0, 20.0);
SearchResults_DustinVP().performTest(2.0, 20.0);
SearchResults_WhittakerVP().performTest(2.0, 20.0);
browser_htmlBrowser(document_bookpoolBooksFound(),DEFAULT_FLAGS).close();
   }
}

 

你还应该在右边的脚本浏览器中看到列出的所有验证点和测试对象。



图21 验证点和测试对象列表
图21 验证点和测试对象列表
 

1. 在功能测试项目视图中右键点击脚本并在弹出的菜单中选择运行。



图22 点击 VerifySearch脚本后选择运行
图22 点击 VerifySearch脚本后选择运行
 

2. 这将打开选择日志向导。点击完成接受默认信息。

Functional Tester此时应掌握控制并执行测试。执行完毕后,回放日志文件应打开并显示发生的一切。如果由于一些原因这没有发生,把你的代码与列表1中的进行比较,确保你的所有命令都是正确的。



图23 VerifySearch脚本的回放结果的第一部分
图23 VerifySearch脚本的回放结果的第一部分
 

测试二:检验向购物篮中添加一件货物

现在你将要建立一个测试脚本来检验你是否可以向你的购物篮中添加一件货物。在这个测试中,你将打开网站,搜索一本特定的书,把书加入你的购物篮,并检验购物篮加载了你的货物以及正确信息。

搜索

1. 右键点击RegressionScripts文件夹,在弹出的菜单中选择使用记录器添加脚本。

2. 在"记录新的Functional Test脚本"向导中,输入脚本的名称(在我们的例子中,使用VerifyBasket),然后点击完成。

3. 点击启动应用程序图标。

4. 在"启动应用程序"向导画面选择BookPool.com应用程序并点击确定。

5. 当浏览器完成下载后,点击搜索域,输入Lessons Learned in Software Testing作为搜索文本,然后点击Search按钮。

6. 在结果画面上,点击在 Lessons Learned in Software Testing 书名旁边的Add to Basket 按钮。

创建一个验证点

现在你将要检验货物被列入了购物篮中。

1. 对包含该书的表格单元使用可见文本选项来记录数据验证点。



图24 结果表格,单元用红线圈出
图24 结果表格,单元用红线圈出
 

2. 使用可见文本选项记录数据验证点来确保画面顶部的购物车图标已按照你购物车内的货物进行了更新。



图25 购物车图标的文本
图25 购物车图标的文本
 

3. 一旦检验完成,你需要把货物从你的购物车中移走。这个网站使用cookies来记录你的购物车信息。因此,如果你现在不把它移除,下次你访问网站时它还将在你的车里并影响下一个脚本的运行。要移除这件货物,只需点击Remove按钮。

完成并运行脚本

1. 关闭浏览器。

2. 点击功能测试记录监视器中的"停止记录"图标。

Functional Tester此时应该产生一个与列表2近似的测试脚本。



列表2 刚刚记录的VerifyBasket回放脚本
 
package RegressionScripts;
import resources.RegressionScripts.VerifySearchHelper;
public class VerifyBasket extends VerifyBasketHelper
{
public void testMain(Object[] args) 
   {
// HTML Browser
startApp("www.BookPool.com");
// Document: Bookpool: Discount Computer Books. Welcome!:
http://www.bookpool.com/.x/nqbosfkvb8/ho
text_qs().click(atPoint(120,9));
browser_htmlBrowser(document_bookpoolDiscountCompu(),DEFAULT_FLAGS).inputChars
("Lessons Learned in Software Testing");
button_search_btnGif().click();
// Document: Bookpool: Books Found: http://www.bookpool.com/.x/nqbosfsev1/ss?qs=
Lessons+Learned+in+Software+Testing&x=22&y=12
button_addToCart2Gif().click();
ShoppingBasket_LessonsLearnedVP().performTest(2.0, 20.0);
ShoppingBasket_IconTextVP().performTest(2.0, 20.0);
// Document: Bookpool: Shopping Basket: http://www.bookpool.com/.x/nqbosf15nm/ba/0471081124
button_remove().click();
browser_htmlBrowser(document_bookpoolShoppingBaske2(),MAY_EXIT).close();
   }
}

 

运行脚本以确保它是正确的。

测试三:检验主题菜单的内容

现在你将要建立一个测试脚本来检验网站主页的主题菜单的内容。在这个测试中,你将打开网页,然后简单地在整个菜单的内容上完成一次检验。

打开Web站点

1. 右键点击RegressionScripts文件夹,选择使用记录器添加脚本。

2. 在"记录新的FunctionalTest脚本"向导中,输入脚本的名称(在我们的例子中,使用VerifySubjects), 然后点击完成。

3. 点击启动应用程序图标。

4. 在"启动应用程序"向导画面选择BookPool.com应用程序并点击确定。

建立一个验证点

1. 当浏览器完成下载后,点击插入验证点或动作图标。

2. 使用对象查找器,选择主题表格。(我必须来到表格的最下面来选择整个表格。)



图26 用红线圈出的主题表格
图26 用红线圈出的主题表格
 

3. 在"验证点和动作"向导中,在"选择一个动作"标签中选择"执行数据验证点",并在"插入检验点数据命令"标签中把数据值改为数据网格。

4. 点击下一步将看到"验证点和动作"向导的"验证点数据"标签。这一次你将在右边的面板中看到一个表格,它包含了你使用对象查找器从表格中选择的文本。



图27 带有主题表格的验证点数据标签
图27 带有主题表格的验证点数据标签
 

5. 双击表格中的第一个单元(内容是"Artificial Int:"),会出现另一个属性窗口。



图28 表格第一个单元的属性窗口
图28 表格第一个单元的属性窗口
 

6. 在这个窗口里,主题菜单的所有文本都以单元的数据属性值出现。选择所有文本把它们复制到记事本。你应该看到菜单中的所有根节点以及它下属的所有选项。这就是你要检验的信息。关闭窗口。

7. 对其它带有文本的单元进行相同的检查,你将发现它们都包含菜单下表格中也包含的那些项。你不需要检查那些元素,因此你需要限制你的选择标准。使用右上角的下拉菜单把选择标准由原来的"列选择"改设为"单元选择"。

8. 选择表格的第一个单元并点击下拉菜单左边的"更新比较区域"图标。你的选择应该如下图所示:



图29 验证点数据标签中选择的表格的第一个单元
图29 验证点数据标签中选择的表格的第一个单元
 

9. 点击完成。

完成并运行脚本

1. 关闭浏览器。

2. 点击功能测试记录监视器中的停止记录图标。

Functional Tester此时将产生一个近似于列表3的测试脚本。



列表3 刚刚记录的VerifySubjects回放脚本
 
package RegressionScripts;
import resources.RegressionScripts.VerifySearchHelper;
public class VerifySubjects extends VerifySubjectsHelper
{
public void testMain(Object[] args) 
   {
//HTML Browser
startApp("www.BookPool.com");
EntireSubjectsGridVP().performTest(2.0, 20.0);
browser_htmlBrowser(document_bookpoolDiscountCompu(),MAY_EXIT).close();
   }
}

 

运行脚本以确保它是正确的。

建立一个完整的回归测试脚本

现在你有了三个可以工作的功能测试脚本。这三个脚本将作为你的回归测试的基准。尽管如此,当你做好准备运行你的回归测试时,你很可能有上百个回放脚本而不希望一个一个运行它们。取而代之地,你需要一种一次运行所有脚本的方式。有几种不同方法实现这个目的。这里我将介绍一种方法。

1. 在功能测试项目视图中,右键点击BookPool项目并选择添加空脚本。

2. 为脚本命名(在我们的例子中,使用BookPool_CompleteRegression)并点击完成。

3. Functional Tester建立一个空白脚本,与列表4中的近似。



列表4 空白回放脚本
 
import resources.BookPool_CompleteRegressionHelper;
public class BookPool_CompleteRegression extends BookPool_CompleteRegressionHelper
{
public void testMain(Object[] args) 
   {
// TODO Insert code here
   }
}

 

4. 删除// TODO Insert code here注释。

5. 当光标还在那一行时,在功能测试项目视图中右键点击RegressionScripts文件夹并选择将包含的脚本作为"callScript"插入。

6. 在接下来的对话框中点击是。

7. Functional Tester向文件夹中的每一个脚本插入callScript命令,如列表5 所示。



列表5 更新的脚本,能调用你的功能测试脚本
 
import resources.BookPool_CompleteRegressionHelper;
public class BookPool_CompleteRegression extends BookPool_CompleteRegressionHelper
{
public void testMain(Object[] args) 
   {
callScript("RegressionScripts.VerifyBasket");
callScript("RegressionScripts.VerifySearch");
callScript("RegressionScripts.VerifySubjects");
   }
}

 

8. 运行新的脚本。回放日志应该反映了发生的所有情况。

可复用的测试框架

可复用的测试框架是软件测试的高级主题,它的目的是促进软件测试的重用性和效率。本章节将对可复用的测试框架的概念进行介绍,并介绍使用 Functional Tester 实现一个可复用的测试框架的方法。

可复用的测试框架介绍

由 John D. McGregor 博士创建的测试设计模式 Design an architecture for object-oriented component testing规定了以下内容:"测试软件的基础体系结构常常设计成有益于跨许多应用程序和测试解决方案。一般的功能是作为测试许多不同类型组件的基础并作为标准工具的接口。" 同许多测试设计模式一样,该模式说明了测试代码的重用性,并阐明了可能的解决方案:创建了一个可复用的测试框架。

使用测试模式方法

在你创建可复用测试框架之前,你可能会想:"我该从哪里开始?如何进行?存在可用的指导吗?"。幸运的是,在你之前已经存在了一些可以借鉴的工作。在其他类型的软件设计模式中,已经创建了许多测试设计模式。当然,它们并没有包括创建可复用的测试框架所有需要的东西。你需要做的是选择一种适合你的测试设计模式作为构建你自己的测试框架的开始。同时你需要选择一种能够方便的为创建测试框架提供良好支持的测试工具。Rational Functional Tester 就是一个非常易用,并且功能强大的自动化功能测试工具,它将是你的一个很好的选择。

可复用测试框架的需求

在你选定了工具(Functional Tester)并且有了设计方法(适合你的模式)。是时候来看一下创建一个可复用的测试框架的需求了。我们的测试框架应该具有以下功能:

  • 自动的测试输入生成
  • 自动的预期结果生成
  • 自动的比较器

这些功能出自 Robert V. Binder 的 Testing Object-Oriented Systems Models, Patterns, and Tools。书中列出许多更多的需求,但是,其他的需求的要么是由 Functional Tester 自动处理,要么对一般解决方案不是必要的。

自动测试输入生成

在 Functional Tester中,脚本被记录,并且测试人员执行以下工作:将脚本模块化、数据驱动想得到的输入、创建验证点,并回放脚本。然而,如果测试用例的数量很大的话,问题就会出现。在此情况下,记录所有测试用例步骤是不现实的。当关联到许多测试工程上时,该问题再次扩大了。对此问题的解决方案是提供用于生成可在所有测试工程中复用的测试输入的一般框架。

自动生成测试输入有三个主要的方法:

  • Functional Tester 数据库,可以由 IBM Rational TestManager 数据库导入。
  • 通过简单地检索 XML 或关系数据来输入信息。
  • 输入数据算法的生成

方法一:使用: Functional Tester 数据库输入信息

Functional Tester 数据库用于生成不需要由复杂算法(例如 Address或 Phone 字段)生成的可变数据。你可以利用 IBM Rational TestManager 数据库创建的自动格式化特性来创建简单的模式,如社会安全码、名和姓、地址和邮政区码。然后,Functional Tester 可以导入这些数据库。

导入数据库




 

在导入之后,使用 Functional Tester 关联数据库和你的脚本。Functional Tester 允许你选择哪一项用数据库值来替代。你可以通过索引或脚本中的名称查阅到数据库字段。在形成参考之后,如果需要,这些值可以通过编程进行变更。

方法二:通过简单地检索 XML 或关系数据来输入信息

一个实例是自动生成持续的帐户或一般的分类帐户,在其中嵌入了一些语义,并且希望在测试数据中维持真实性,特别是数据涉及非常大的必须在测试程序执行之间保持顺序的数字。此处最好的解决方案是创建测试数据库,并通过编程的方式地从数据库的记录中获取输入数据,执行任何你的测试需要的语法分析,在 Functional Tester 中按需要使用数据。在 Functional Tester 中从一个脚本到另一个脚本进行数据更新,并将在下一个测试程序执行中使用的数据行插入到测试数据库中。

如果你想要为测试框架提供来自 Functional Tester 脚本的基于 SQL 的数据库调用的最大化的抽象,你可以使用Mapper 和 Query Object设计模式,它允许你对 Functional Tester 脚本中的信息进行一般化的请求,并服从具体数据库 SQL 语法到其他类的结构。

方法三:输入数据的算法生成

通过算法从 XML 或关系数据库中检索数据来有计划地生成输入数据是一般测试框架的核心引擎。

一种有效的存储用于一组 GUI 控件的可能输入值的方法是将控件及其可能的值放入 XML 文件中。你可能想要通过任意组合框的下拉列表提交值。对于文本框,你可以提交导致各类业务规则结果值的具体实例(比如,提交 1 和 12,如果业务规则规定 0 和 10 之间的值会导致一个结果,而 11 和 20 之间的值会导致另一个结果)。

要访问 XML 文件,我们将利用文档对象模型(Document Object Model,DOM)。此方法可以将所有 XML 访问代码保留在 Functional Tester 类中。使用 DOM 的通常的劣势是需要许多内存,但是,当应用程序定义 GUI 控件的 XML 文件很小的时候,内存就不成问题了。如果你要测试经常变化的应用程序,你要虑及更为简单的维护,来为每一屏创建单个的 XML 文档,或一些其他的逻辑分割。一旦你了解了XML文档遍历的方法,对包含 GUI 控件和这些控件可能值的 XML 文件的 DOM 分析就相当的简单了。基本操作是,你创建类来设置 DOM 使其与 XML 文档一起工作,过滤 XML 节点,并处理过滤器的结果。

减少生成的测试用例的数量

当然,处理测试输入的所有可能的组合输出是不现实的。例如,如果你有十个参与的 GUI 控件,并且如果每个控件只取三个可能的值,你将生成 59,049 个测试用例!有许多方法可以限制用例的数量。最简单的方法是在 XML 文件中加入标志,只考虑临界的 GUI 控件和值。另一种方法是让框架生成所有的测试用例,但只处理第一个 n,或者只处理随机选择的用例。其他方法还涉及在代码中调用生成测试用例的业务规则(例如,"如果 GUI 控件 A 的值小于 20,但 GUI 控件 B 的值大于 12,那么删掉该测试用例")。

预期结果的自动生成

一旦创建了用于自动生成测试输入的类,你就可以创建使用这些输入的 Functional Tester 脚本了。测试框架的整个架构将有一个控制器脚本,这个控制器脚本将执行两个功能:1) 在先前的测试脚本之执行之后进行清除,并设置后续的脚本,和 2)调用其它模块化的测试脚本。这些模块化脚本越是紧密,就越能更好地促进脚本的复用性。用于调用其它脚本的 Functional Tester Java 语法是:callScript ("OtherModularizedScriptName");。

假设被测的应用程序有大量的测试用例,Functional Tester 使我们实现 Prototype 设计模式以在测试执行时选择期望的功能测试方法。这可以通过利用反射机制在 Functional Tester 助手(Helper)类中参数化地调用方法来完成。在记录脚本时,Functional Tester 会自动生成这些助手类。

Functional Tester 助手类中包含了到被测应用程序中的 GUI 对象的索引。Functional Tester 向测试对象图中添加 GUI 控件的概念有效地实现了的 Identity Map 设计模式。Functional Tester 通过测试人员执行的两步骤的过程获得了这些 GUI 对象的属性:

1. 使用 Functional Tester GUI 控件识别工具。更确切地说,将"手形"图标拖到控件上,释放以向 Functional Tester 测试对象图中输入对象。

2. 将测试对象图添加到需要访问该控件的测试脚本中(在 Functional Test 透视图顶部菜单栏:选择脚本 > 打开测试对象图 > 选择一个对象 > 添加到脚本)。

拖动手形选择方法




 

利用上面介绍的过程,你应该能够快速的创建模块化的、可复用的,由你所创建的测试脚本控制器调用的脚本。然而一个错过的内容是动态控制测试程序的方法。对此,你的测试框架可以使用一个 GUI 接口,通过它测试团队可以控制运行时参数(当然,在你的测试控制器脚本启动时,Functional Tester 也允许你输入命令行参数)。

自动比较器

既然你的测试框架中包含了输入测试用例数据和执行测试用例的一般化机制,那么你就需要一个自动验证结果的方法。存在许多比较基线数据和实际结果的可用选择。Functional Tester 提供三种类型的验证:静态、手动和动态。要创建一个一般的测试框架,你最好的选择可能是手动,因为其考虑最大化的可编程的控制和有效的、高容量的验证。当然,你也可以编写自己的代码,但 Functional Tester 方法已经将结果写到回放日志中。因此,你的测试框架会包含如下的脚本代码:vpManual("vpResultChecker1", strExpectedResult, strActualResult).performTest();

然后,你将从 XML 文件或关系数据库表格中读到预期的结果。你会想将每个测试用例的ID 与响应的期望结果关联起来。

IBM Rational Functional Tester 的调试特性

调试可以与设置一个断点停止脚本执行一样简单,因此你可以看到在测试执行过程中的一个特殊点上发生了什么。

你是否曾经有过仅仅通过查看代码不能解决的一个脚本问题?你真的想看到在运行时发生了什么。如果你有这样的想法,调试能够很容易地设置一个断点来中断脚本的执行,因此你能看见在执行过程中一个特殊的点发生了什么。本章节描述使用Functional Tester 调试脚本的过程。

调试Functional Tester 脚本需要你像个开发人员一样思考。Functional Tester 产生的脚本源文件是纯Java的。在这里提到的大多数概念都是一个Java开发人员所熟悉的,如果之前你已经学习过Java 编程,这将是很好的基础。

调试基础

调试需要几个命令。在进行之前理解这些概念是很重要的。

断点

一个断点是在你的源代码里的一个标记,用以确定在哪一点上停止代码执行。当你知道问题发生的区域时,这个命令是很有用的。你在一行语句上设置一个断点,接着在调试器中运行脚本,它会在到达此点时自动地停下。

单步执行(Step Over)

单步执行(Step Over) 是一个高层的、按照先后顺序一行一行执行的脚本操作。

单步跳出(Step Out)

单步跳出一个函数将会把你带回到函数被调用的点。当你意外地单步进入一个函数时,或者当你确信这个函数的其余部分将会成功运行时,这个命令就是有用的了。

单步进入(Step Into)

单步进入将跳进当前行或语句中的详细事件。当你进入一个函数调用时,它会打开源代码,并且开始逐步执行代码行。

单步进入是非常容易让人糊涂的功能,因为一些代码行包含了复杂的函数,让我们看一个简单的代码行。

代码行例子




 

这行代码有三个明显不同的函数,并且可能还会有更多,这些结果都会被执行出来。从左边开始,你会看到tree2()函数,它取得了一个对象,此对象表示应用程序的一个树状菜单。这个对象有一个click()函数,它使用一个参数来定义在菜单树上什么位置发生了点击。最后,我们有atPath()函数,它识别了才菜单树上点击的位置。

如果你尝试单步进入这行代码,你将不会单步进入到click()函数,至少不是最先进入。click()函数一直到我们取得tree2对象才会存在,并且click()函数直到我们决定了atPath()函数的参数值才能被调用。

一些被调用的函数可能属于系统库或组件,可能没有可得到的源代码。如果发生了这样的情况,只要使用单步跳出(Step Out)功能返回到调用函数就可以了。

调试过程

进入到详细的函数完全是被迫的。为了避免这样的问题,你可以遵循一个简单的递归调试技术。遵循这个方法会逐渐缩小你的关注范围,直到你最终找到问题点。对于那些富于编程和调试经验的人来说,递归调试可能不是最快的方法,但是对于解决问题的初学者来说这是个可靠的过程。

使用这个方法,你每次通过一层代码,确定产生问题的下一层。重复这个过程直到你找到问题的根源。

递归调试过程

1. 从脚本中的一个已知点开始,并设置一个断点。

2. 启动调试器,并让它运行直到到达这个断点。调试器将会在这个断点行突然出现。

3. 使用单步执行(Step Over)功能,注意你正在执行的行,直到产生了错误情形。这个错误一定是在你刚执行的行中的一个函数里出现的。

4. 打开标注在这行的函数的源代码,并在函数内部设置一个断点。

5. 重新启动调试器并运行,直到你到达新的断点。

6. 重复这个过程,直到你找到产生错误情形的行。

使用Rational Functional Tester进行调试

ClassicsOnline项目

为了使你能够简单的进入调试操作,我准备了一个名为ClassicsOnline 的项目。 你必须导入ClassicsOnline项目。对此项目所包含的内容快速浏览一下,你应当发现三个脚本。所有这三个脚本都是按照Classics OnlineA应用程序记录下来的。

ClassicsATestScript: 这个脚本是项目的主要测试脚本。在此脚本中,我们启动Classics 应用程序,验证composer/album树的内容,检查一张CD里的详细内容,并下一个订单。我们使用一个脚本来登录并且用另一个脚本输入信用卡信息。在测试脚本的最后,我们关闭这个应用程序。

ClassicsAMemberLogin: 此脚本从一个数据池中读取用户名和密码,并在Member Logon屏幕中输入信息。

ClassicsAEnterCreditCardInformation:此脚本从一个数据池中读取信用卡的信息并将数据输入到Place an Order屏幕中。当下订单时,它会打开订单确认对话框。

在我们的例子里,我们在一些数据中包含了一个错误,用以举例说明下面的调试技术。另一个调试情形是在按照应用程序的不同版本回放我们的脚本时引入的。我们知道,使用的例子是什么并不重要,并且大多数测试人员不使用任何调试技术就能解决他们。但是,我们的目的不是查看困难的代码情形,而是要简单地学会调试思维过程和技术。我们选择简单的缺陷,来证明一些公共的问题,并且因为代码和测试的程序都是简单的,你就可以集中于如何调试,而不是调试什么。

调试环境介绍

让我们开始回顾调试环境。打开ClassicsATestScript脚本文件,并打开Functional Test 调试透视图。

1. 在脚本打开后,选择开打透视图按钮,它在Functional Tester 的右上角处。

2. 如果Functional Test 调试不是一个选项(这是可能的),选择其他...

3. 选择Functional Test 调试并点击确定。



图3. 选择透视图对话框
图3. 选择透视图对话框
 

默认的透视图布局包括很多视图。在左上角是调试视图,它是你的调试活动的主要控制点。



调试视图
调试视图
 

在右边,是变量、断点和表达示视图。在我们开始调试时,这些视图提供了有关上下文环境重要信息。



变量、断点和表达示视图
变量、断点和表达示视图
 

这个屏幕的其余部分看上去像是Functional Test 透视图,脚本源文件在中间,脚本浏览器在右边,并且控制台,任务, 和问题视图在底部。



源脚本,脚本浏览器,控制台,任务,问题
源脚本,脚本浏览器,控制台,任务,问题
 

在调试模式下运行

第一步是更改我们正在测试的应用程序的版本。在脚本的顶部,将startApp命令的参数应用程序的名字改为ClassicsJavaB。现在你可以像往常一样通过点击工具栏中的运行功能测试脚本图标来运行脚本了。

在你第一次运行脚本测试ClassicsJavaB 程序时,可能发生两个错误。第一个可能的错误(并且是最可能的)是在 "Place an Order" 页面的得到一个无效的产品有效期的错误。



无效的产品有效期错误
无效的产品有效期错误
 

第二个可能的错误是间歇发生的(并且是难以调试的)。这个问题在尝试用一个已有顾客登录时不时会发生。我们将会依次看一下每一个问题,但是记住,你必须像你不知道问题是什么一样来进行。

问题1:无效的产品有效期

让我们回到一个已知的状态。关闭Classics Online 程序,并且回到Functional Tester。假设这个练习是错误发生后,而你并没有看到真实的应用程序。进一步假设这个测试运行了整个晚上,并且计算机在我们进来前(非常有可能)已经重新启动过了。唯一的有用信息包含有日志文件。基于这些信息,我们来进行这个调试过程。

第一步:识别问题是什么

从日志文件中,我们知道在ClassicsAEnterCreditCardInformation.testMain中有一个未处理的异常。因此我们知道在信用卡数据里有一个问题。它在我们的测试或这个应用程序的变更方面是个问题。

第二步:确定问题可能在什么地方

日志文件已经指向了ClassicsAEnterCreditCardInformation脚本。因此,这看上去是开始查找问题的好地点。

第三步:设置断点并走查代码

打开调试透视图中的脚本进行走查,并设置你的断点。

1. 在Functional Tester 的左上角,点击Functional Test 项目视图来激活它。



Functional Test 项目 视图
Functional Test 项目 视图
 

2. 双击ClassicsAEnterCreditCardInformation脚本来打开它。这应当会打开一个脚本,看上去如下所示:



脚本的例子
脚本的例子
 

3. 接下来,在脚本中的第一个可执行代码行上设置一个断点。在代码左边的灰色区域上,紧邻着你想要暂停的代码行,右键点击并选择切换端点。这应当会插入一个蓝色的小圆圈在代码行上:



插入断点
插入断点
 

4. 切换返回到ClassicsATestScript,并使用工具栏中的调试 Functional Test 脚本图标来运行脚本。

5. 选择一个日志文件并点击完成。

6. 脚本应当正常运行。当测试到了Place an Order页面时。Functional Tester 将会再次变成激活,并且你应当会看到在早先我们所看到的所有视图的所有信息。

在调试视图中,你看到一列线程节点。当你达到一个断点时,主线程被暂停了,并且显示一个堆栈追踪。堆栈追踪显示了执行的当前行,以及被进行到这里的路径。

第一行显示出,我们处在ClassicsAEnterCreditCardInformation类的testMain函数的第32行。每个附加行代表了我们进行到达当前执行点的一个函数,并且如果类是在调试模式下编译的,你也会看到那个函数中的代码行,这是发生调用把我们带到下一级别的地方。



调试视图
调试视图
 

注意,在调试视图中现在有一个活动的调试工具栏。使用这个工具栏,你能恢复或停止回放,并且你能单步执行,单步进入,单步返回(或者单步跳出)一个方法。



调试工具栏
调试工具栏
 

在变量视图中,你可以看到当前选中的堆栈框架的可视变量。变量视图显示了基本类型的值。复杂的变量可以通过扩大它们显示它们的成员来查看。



变量视图
变量视图
 

下一个是脚本源,你应当在你的断点的最上方看到一个箭头。这个箭头显示了在你的脚本执行中的当前位置。任何时候你执行一个单步命令(单步执行,单步进入,等等),你就会指向由此箭头所指示的代码的当前行。



当前位置箭头
当前位置箭头
 

现在,我们可以逐行地浏览ClassicsAEnterCreditCardInformation脚本,以准确地查出哪一行代码有问题。对于以下代码:



 
cardNumberIncludeTheSpacesText().setText(dpString("CardNumberIncludeTheSpacesText"));
creditCombo().setText(dpString("creditCombo"));
placeOrder().click();

 

7. 我们执行一次单步执行(Step Over),并看一下在屏幕上实际发生了什么。

8. 点击单步执行按钮执行代码的第一行。这一行代码对数据池执行了一个调用,并将返回值输到屏幕上的Card Number字段。

9. 当执行时,切回到Functional Tester,可以看到指示代码中的当前行的箭头已经移到下一行。再次单步执行,并为Card Type输入数据池值。

10. 再次切回到Functional Tester,可以看到我们现在处在代码的下一行上。再单步跳过,从数据池输入Expiration Date值。

11. 单步跳过几次下此订单。我们现在可以更清楚地看到错误。



无效的产品有效期错误
无效的产品有效期错误
 

问题是,在Expiration Date字段中输入的值 "1212" 不符合任何有效的产品有效期格式。

12. 既然我们已经识别出了问题,我们就可以结束调试过程,进入我们的数据池,并修复产品有效期。

13. 当你结束调试时,Functional Tester返回到Functional Test透视图中。

14. 在ClassicsAEnterCreditCardInformation脚本的数据池值中修复格式。

15. 在你再次运行你的脚本之前,要确认清除断点。要完成此操作,在断点上右键点击,并选择切换断点。

16. 运行ClassicsATestScript脚本,并验证bug确实被修复了。

第四步:重复步骤三直到发现问题

我们第一次尝试发现了我们的问题。使用记录和回放脚本的方式并不是很少见的,但是随着你的脚本在复杂性上不断增加,这就会变得更棘手。如果我们还没有发现问题,我们可能已经缩小或完全转变了我们所关注的范围。

问题二:捕捉一个间歇发生的bug

我们现在已经修复了无效的产品有效期,让我们尝试捕捉在Member Logon页面上的间歇发生的bug。在这点上,如果你还没有看到在Member Logon页面上的间歇发生的bug,向前走,并多次运行你的脚本,直到你在你的日志文件中发现一个错误。当你准备好时,花一些时间,继续进行。

第一步:识别问题是什么

从日志文件中,我们知道在ClassicsAMemberLogin.testMain中有一个例外处理。因此,我们知道我们使用登录脚本或登录数据有了一个问题。此外,我们的测试或应用程序的一些变更也可能是一个问题。

第二步:确定问题可能在哪里

日志文件已经把带向ClassicsAMemberLogin脚本。因此,这看起来像是一个开始寻找问题的好地方。

第三步:设置断点并走查代码

打开调试透视图中的脚本进行走查,并设置你的断点。

1. 打开ClassicsAMemberLogin脚本,并在代码的第一行设置你的断点。



设置断点
设置断点
 

2. 切换返回到ClassicsATestScript,使用工具栏中的调试 Functional Test 脚本图标运行脚本。

3. 选择一个日志文件,并点击完成。

4. 当测试走到Member Logon页面时,Functional Tester应当在调试透视图中再次激活。

5. 点击单步执行执行代码的每一行。

当你走到ClassicsAMemberLogin脚本的末尾时,你有可能或还没有碰到真正的错误。假定由于例子的缘故,你没有碰到错误。在你已经完成单步跟踪ClassicsAMemberLogin脚本之后,点击恢复以完成脚本执行。

第四步:重复步骤三直到发现问题

我们没有发现我们的问题,因此我们将尝试一种不同的方法。我们知道问题是在脚本中的,但是我们不知道问题是否在我们正在使用的录制代码或数据中。因为问题是偶尔发生的,我们可能怀疑问题是与数据相关的。让我们试着对我们的数据增加一些可试性。

1. 在ClassicsAMemberLogin脚本中更改代码。你只想将这些数据池值读到变量中,因此你可以在执行期间在变量视图中看到它们。代码看来可能如下所示:



ClassicsAMemberLogin脚本
 
public void testMain(Object[] args) 
	{
		// Declare variables
		String nameOfUser = null;
		String userPassword = null;
		
		// Frame: Member Logon
		existingCustomer().click();
		
		// Read data from datapool
		nameOfUser = dpString("nameCombo");
		userPassword = dpString("PasswordText");
		
		// Enter data
		nameCombo().select(nameOfUser);
		passwordText().setText(userPassword);
		ok().click();
	}
	

 

2. 在第一行代码处重新设置你的断点:



重新设置断点
重新设置断点
 

3. 切换返回到ClassicsATestScript,并使用工具栏中的调试 Functional Test 脚本图标运行脚本。

4. 当测试走到Member Logon页面时,Functional Tester应当在调试透视图中再次激活。

5. 点击单步执行按钮执行代码的每一行。

6. 现在,如果你在变量视图中查看,你应当看到我们刚才创建的变量,并且它的值应当是空的。



变量视图-空值
变量视图-空值
 

7. 再次单步执行以创建密码变量。

8. 点击Existing Customer,单步跳过此行。

9. 单步跳过这些读数据池值的行。你现在应当看到在变量视图中装载的那些值。



变量视图-数据池值
变量视图-数据池值
 

注意userPassword是红色的。这说明此变量的值刚才更改过(你单步跳过的代码的最后一行将它从null更改为 "John" )。

10. 在这点上,我们知道我们正在从Full Name字段中选择值 "John Smith" ,并且我们将要在Password字段中输入 "John" 。在执行任何更多的代码之前,切换到Member Logon页面,并手工验证这些事实上是有效的值。

11. 如果你点击Full Name下拉列表,你会注意到 "John Smith" 不是一个有效的值。发现bug!

Bug偶尔发生的原因是数据池值是随机选定的。因为在数据池中有三个名字和密码的组合,而三次只有一次机会选中 "John Smith" 。我们所有必须做的就是更新我们的数据,并且我们的脚本应当正确地执行。

调试提示和技巧

关于断点

断点可以通过从所需行的左边空白处使用右键点击菜单来设定或取消。设定一个断点与增加或删除一个断点是相同的操作。



设定断点
设定断点
 

断点可以被激活或取消激活。程序执行不会停在一个取消激活的断点上。

断点是一直存在的。当一行代码的断点已经被设定时,它会对所有调试会话一直保持着,直到你取消断点。你可以在断点视图中通过右键点击菜单选择全部除去选项来删除所有断点。



断点视图
断点视图
 

你可以用两种方式访问扩展的断点功能。你可以在一行有断点的代码的左边空白处右键点击,或者你可以右键点击并从断点视图中选择属性。扩展的功能包括定义复杂的条件逻辑,以建立将会引起脚本中断的标准。




 

在控制台视图找到问题

有时最好的线索可以在控制台视图中发现。在一个抛出异常的例子中,可能会有一个堆栈跟踪或其它有价值的信息。

死循环是很难进行跟踪的。通常你将会看到一个StackOverflowException,但是可能没有任何有用的堆栈详细信息来帮助你发现源头是什么。

NullPointerException异常是一个常见问题。考虑以下代码:



代码例子
代码例子
 

如果数据池中的nameText的值是空的会怎样?那将会使得对象数据是空的。如果数据是空的,我们就不能使用它的任何功能,至少除了创建一个NullPointerException异常不会有别的。

那么我们应当如何修复这个问题呢?一个容易的办法是在尝试访问data的功能之前检查其是否为空。另外一种方法是回到逻辑上,例如("John Smith".equals(data))将会对初始的代码产生相同的结果,但是决不会抛出一个NullPointerException。

总结

本文已经介绍了可复用测试框架的概念和使用 Functional Tester实现复用测试框架的方法。实现一个可复用测试框架是非常复杂的事情,它涉及到很多高级的概念和实际的开发经验。如果你对可复用测试框架感兴趣,你需要学习更多的软件测试和开发方面的知识,包括软件测试策略、软件设计模式、面向对象的开发等等。

同时大家对如何使用调试器有了一些了解了,大家可以使用这些知识来调试更复杂的脚本,你可能已经为你的应用程序创建了这些脚本。在调试测试脚本过程中重要的事情是,更好地理解你的测试代码是在做什么,而不管你的测试数据是从哪里来的,以及它是如何更改的。

s
 

组织简介 | 联系我们 |   Copyright 2002 ®  UML软件工程组织 京ICP备10020922号

京公海网安备110108001071号