您的当前位置:首页正文

第九章 Functional Tester 调试特性(更新)

2021-08-13 来源:步旅网
第九章 IBM Rational Functional Tester 的调试特性

调试可以与设置一个断点停止脚本执行一样简单,因此你可以看到在测试执行过程中的一个特殊点上发生了什么。本章节将向您介绍 Rational Functional Tester 强大的脚本调试特性,它将帮助您快速、准确的定位脚本中的问题。

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

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

1.调试基础

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

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

单步执行(Step Over)

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

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

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

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

代码行例子

图9.1

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

如果你尝试单步进入这行代码,你将不会单步进入到click()函数,至少不是最先进入。click()函数一直到我们取得tree2对象才会存在,并且click()函数直到我们决定了atPath()函数的参数值才能被调用。 一些被调用的函数可能属于系统库或组件,可能没有可得到的源代码。如果发生了这样的情况,只要使用单步跳出(Step Out)功能返回到调用函数就可以了。

2. 调试过程

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

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

递归调试过程

图9.2

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

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

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

在你刚执行的行中的一个函数里出现的。

4. 打开标注在这行的函数的源代码,并在函数内部设置一个断点。 5. 重新启动调试器并运行,直到你到达新的断点。

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

3. 使用Rational Functional Tester进行调试

ClassicsOnline项目

为了使你能够简单的进入调试操作,我准备了一个名为ClassicsOnline 的项目。 你必须导入

ClassicsOnline项目。对此项目所包含的内容快速浏览一下,你应当发现三个脚本。所有这三个脚本都是按照Classics OnlineA应用程序记录下来的。

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

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

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

3.1调试环境介绍

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

1. 在脚本打开后,选择开打透视图按钮,它在Functional Tester 的右上角处。 2. 如果Functional Test 调试不是一个选项(这是可能的),选择其他...

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

图3. 选择透视图对话框

图9.3

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

调试视图

图9.4

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

变量、断点和表达示视图

图9.5

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

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

图9.6

3.2 在调试模式下运行

第一步是更改我们正在测试的应用程序的版本。在脚本的顶部,将startApp命令的参数应用程序的名字改为ClassicsJavaB。现在你可以像往常一样通过点击工具栏中的运行功能测试脚本图标来运行脚本了。 在你第一次运行脚本测试ClassicsJavaB 程序时,可能发生两个错误。第一个可能的错误(并且是最可能的)是在 \"Place an Order\" 页面的得到一个无效的产品有效期的错误。

无效的产品有效期错误

图9.7

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

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

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

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

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

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

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

目 视图

图9.8

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

下所示: 脚本的例子

图9.9

3. 接下来,在脚本中的第一个可执行代码行上设置一个断点。在代码左边的灰色区域上,紧邻着你

想要暂停的代码行,右键点击并选择切换端点。这应当会插入一个蓝色的小圆圈在代码行上: 插入断点

图9.10

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

本。

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

6. 脚本应当正常运行。当测试到了Place an Order页面时。Functional Tester 将会再次变成激活,

并且你应当会看到在早先我们所看到的所有视图的所有信息。

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

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

图9.11

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

图9.12

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

图9.13

下一个是脚本源,你应当在你的断点的最上方看到一个箭头。这个箭头显示了在你的脚本执行中的当

前位置。任何时候你执行一个单步命令(单步执行,单步进入,等等),你就会指向由此箭头所指示的代码的当前行。 当前位置箭头

图9.14

现在,我们可以逐行地浏览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. 单步跳过几次下此订单。我们现在可以更清楚地看到错误。

无效的产品有效期错误

图9.15

问题是,在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脚本,并在代码的第一行设置你的断点。

设置断点

图9.16

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. 在第一行代码处重新设置你的断点:

重新设置断点

图9.17

3. 切换返回到ClassicsATestScript,并使用工具栏中的调试 Functional Test 脚本图标运行脚本。 4. 当测试走到Member Logon页面时,Functional Tester应当在调试透视图中再次激活。 5. 点击单步执行按钮执行代码的每一行。

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

变量视图-空值

图9.18

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

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

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

变量视图-数据池值

图9.19

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

10. 在这点上,我们知道我们正在从Full Name字段中选择值 \"John Smith\" ,并且我们将要在

Password字段中输入 \"John\" 。在执行任何更多的代码之前,切换到Member Logon页面,并手工验证这些事实上是有效的值。

11. 如果你点击Full Name下拉列表,你会注意到 \"John Smith\" 不是一个有效的值。发现bug! Bug偶尔发生的原因是数据池值是随机选定的。因为在数据池中有三个名字和密码的组合,而三次只有一次机会选中 \"John Smith\" 。我们所有必须做的就是更新我们的数据,并且我们的脚本应当正确地执行。

4. 调试提示和技巧

关于断点

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

设定断点

图9.20

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

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

断点视图

图9.21

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

扩展功能

图9.22

在控制台视图找到问题

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

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

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

代码例子

图9.23

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

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

5. 总结

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

因篇幅问题不能全部显示,请点此查看更多更全内容