第一部分
第一章简介
什么是自动化测试
自动化测试是对一个已有的手工测试过程减少并尽可能排除人工干预的过程。
什么时候适合做自动化测试
下面是一组适合将手工测试自动化的考量因素:
●测试需要经常重复。
●测试流程和验证点相对长时间比较稳定。
●测试目的是验证一个业务流程,而不是外观,感觉,颜色,图表布局等。
●测试需要大量重复或者同时包含很多步骤,并且这些操作每次都需要完全一致,这就要求手工测试者不能疲劳大意。
●测试生成的结果被监管机构要求电子化记录和存档并符合正式的证据要求。
●测试通过或失败的结果相当容易判断且被所选自动化工具捕获。
●测试需要使用大量的数据到被测应用程序中。
什么时候需要避免自动化
●随机性测试,领域专家在各种业务流程组合中的随机尝试。
●一次性测试或者只重复数次。
●测试需要覆盖多个功能模块且这些功能模块在整个产品功能中的测试覆盖几乎非常小。
●测试验证外观,感觉,颜色,图表的布局等。
●测试结果是否通过需要从多个不同并且不相关的系统或(和)应用中判断
自动化测试流程
理解自动化测试中包含的各个阶段对于开发和有效利用测试框架以及用例非常重要:
●选择“最适合的”自动化工具:在对任何应用开始自动化测试之前,重要的是针对主要应用部分选择最适合的工具。选择需要基于各种因素,比如价格,易用性,应用支持能力和产品服务支持。
●概念证明(POC):此阶段包含创建一些脚本示例用来在一两个最重要的被测应用中验证业务流程。它可以帮助识别未来在测试用例的脚本开发中有可能碰到的主要问题。概念证明也可用来为你的应用选择最适合的自动化测试工具。
●需求分析:包含分析某个应用的需求,研究已有的手工测试用例和定义当前自动化测试项目的范围。
●项目估算:一旦自动化范围定义好,项目估算就可以根据各种因素,如需要自动化的测试用例数量,复杂程度,需开发的可复用模块,人员需求等制定下来。
●框架设计:包含创建共享对象库,重用模块,编写最佳实践参考文档,以及实现任何可以对开发自动化测试脚本有用的基础支持组件。
●测试脚本开发:通过调用可重用模块和在工作流具体脚本中增加相关验证点来创建测试用例。
●调试:完成的测试脚本应该是经过调试的,以保证运行时符合预先设计。要确保在调试时使代码经过所有错误处理路径。
●执行:在这个阶段测试脚本最终在回归测试中执行来验证被测应用。
●结果分析:此阶段流程依据执行时生成的各个测试结果。
●维护:这个阶段包括更新脚本来解决执行中发现的代码问题,其中可能包含UI或结构变动,或者是流程,功能以及新版本中不可避免的变更。一个设计良好的框架和测试集可以保证维护成本达到最小。
第二部分
第三章对象库
QTP在对象库(OR)里为每一个被测对象存储了一个对象定义。该定义包含了一些用来唯一识别运行时对象的参数值。QTP Object Repository Manage r是用来查看和修改对象库中的对象及其属性的。
图3-1. Object Repository Manager
图3-1,展示了一个简单的对象库。这个对象库有一个WinToolbar对象,包含了一个可以用来识别的逻辑名"Running Applications"和两个属性:"Text" 和" nativeclass" .我们可以点击"Add/Remove"按钮来添加或者删除属性。图3-2显示了从Object Identification打开的Add/Remove Properties对话框,它可以用来
添加或删除任意的属性。
提示:在对象库的树视图中选择一个对象后,点击'Highlight'按钮,应用程序(必须是打开的)中的对象将会高亮显示。同样,在代码中也可以实现高亮:Window("Window").WinToolbar("Running Applications").Highlight.
图3-2.Add/Remove Properties
对象是如何被添加到对象库的?
对象可以通过两种方式添加到对象库:
●通过录制与被测应用程序的交互过程添加。
●手工添加一个或多个对象。
我们可以点击"Add Objects"按钮,然后点击我们要添加的对象,通过这种方式,我们便可以手工添加对象到对象库中。
注意:假如我们要添加的对象是在鼠标点击之后才出现,那么我们可以先按下Ctrl键,然后再去点击。这个方法可以让我们临时屏蔽对象选择模式,从而进行鼠标操作。一旦我们准备好了要添加的对象,就可以放开Ctrl键,进行添加了。
如果我们需要在应用程序间切换,可以先按住Ctrl+ALT键去屏蔽对象选择模式,然后使用例如Alt+Tab键来在不同的应用程序间切换,完成切换后,再次按下Ctrl+ALT键后便可进入对象选择模式并添加对象了。
对象一旦被选中,便会在对象选择窗口中出现
图3-3.对象选择
本对象选择窗口显示了Web页面上的完整的对象结构。选择你需要添加的对象然后点"OK"键就可以了。
小提示:对象选择窗口显示的对象可能会和录制到对象库的不一致。QTP 只保留能识别对象的必须的对象结构,这样就可以在测试脚本使用对象时,减少代码的长度。
如果我们选择一个Page对象,然后继续,QTP就会询问我们是否要添加它的子对象。
图3-4. 对象选择
选择Selected object and all its descendants这个单选按钮,然后点击O K,页面上所有的对象都会被添加到对象库中去。
提示:对象库不能添加页面上的隐藏对象。
测试对象和运行时对象
测试对象(TO):测试对象是QTP定义的一些类,用它们来代表被测应用的各种对象。
运行时对象(RO):运行时对象是实际的被测应用的对象,是测试执行过程中,TO用来关联的对象。
理解这两种对象类型的区别是非常重要的。可以看成两辆车;车A和车B,QTP能在脚本里用一辆车的测试对象来描述出两辆车A和B。除此之外,每个测试对象也提供了用来和运行时对象交互时相关联的方法和属性。
比如Start,Run和Stop都是汽车对象提供的有用的方法。
TO属性
测试对象的属性是QTP为了识别在测试执行过程中的运行时对象而保留在对象库中的属性。QTP提供GetTOProperties方法来列举对象的所有的TO属性.GetTOProperty和SetToProperty则分别用了读取和修改TO的属性值。
问题3-1. Test Object 属性的使用
'获取webeidt对象
Set oWebEdit = Browser("").Page("").WebEdit("")
'获取webedit对象封装属性集合
Set TOProps = oWebEdit.GetTOProperties()
Dim i, iCount
iCount = TOProps.Count - 1
'遍历所有封装属性
For i = 0 ToiCount
'获取属性名
sName = TOProps(i).Name
'获取属性值
sValue = TOProps(i).Value
'检查是否为正则表达式
isRegularExpression = TOProps(i).RegularExpression
'显示结果
MsgboxsName&"->" &sValue&"->" &isRegularExpression
Next
问题3-2. 运行时改变Test Object 属性
'获取webedit对象
Set oWebEdit = Browser("Browser").Page("Page").WebEdit("txtName")
'获取webedit的name封装属性值
oldName = oWebEdit.GetTOProperty("name")
'变更webedit对象的name封装属性
oWebEdit.SetTOProperty"name","new value"
'获取已修改的属性
newName = oWebEdit.GetTOProperty("name") MsgBoxnewName
问题 3-3. 测试中获取运行时对象属性
'x 为WebEdit 对象运行时的value 属性值
x = Browser("").Page("").WebEdit("").GetROProperty("value") MsgBoxx
提示:QTP 不提供修改运行时对象属性的方法。换言之,没有SetROProp erty 这个方法。同样, 不同的测试对象都有一个它支持的属性列表,在QTP 帮助的对象模型参考中可以找到。 对象库模式
有两种对象库,更确切的说是对象库模式。
图 3-5. 每个Action 的对象库设置
每个Action 对应的公共对象库
Action 的对象库
公共对象库
若对象还为添加到对象库,那么可以添加到Ac tion 对象库。
若对象还为添加到对象库,那么可以添加到公共对象库。
重命名对象不会影响其他的脚本。当前的脚本会自动更新对象的命名。
重命名对象并不会更新所有使用公共对象库的脚本,所以这会有比较大的影响。
如果被测对象的属性发生改变,那么需要更改所有脚本。
被测对象的属性发生改变,将会更新到所有脚本中。
如果不是很多的测试脚本来运行同一个应用程序的情况,可以使用Action对象库。
如果有很多不同的脚本都在与相同的对象进行交互,那么建议使用公共对象库。
公共对象库可能会变的很大,所以需要经常备份以免损坏。
对象探测器(Object Spy)
对象探测器是用来查看对象所支持的方法和属性。启动对象探测功能:Too l->Object Spy…
点击指针按钮,然后选择一个对象。当选择了Test Object Properties按钮,属性标签页中就会显示出所有可得到的TO属性,并且在方法标签页中会显示所有可获得的方法,如图3-6
图3-6. 对象探测对象属性
若选择了Run-time Object Properties按钮,那么将会显示对象的实际属性或方法,如图3-7
Figure 3-7. 对象探测实际属性
大多数的属性值可以通过GetROProperty方法获得。想要了解对象支持的所有属性,可以参考QTP手册。
'获取对象运行时的outerhtml封装属性值
sOuterHTML = Browser("").Page("").WebEdit("").GetROProperty("outerh tml")
小提示:对象探测器不会显示出任何带有序数识别的属性,例如CreationT ime, index or location.
它们只能在添加到对象库以后计算出来。
第三部分
对象识别(Object Identification)
对象识别是实现测试脚本的关键部分。QTP不能随意的录制对象,它在记
录对象的一组属性时是遵循一定结构的。我们可以更改这些属性,以适应应用程序。更改设置可以在Tools->Object Identification…
有三种类型的属性可以被QTP用来识别对象:
●强制属性- 强制属性总是被捕捉并保存,即使没有其中的一些属性,对象也能识别也不例外。
●辅助属性- 假如强制属性不足以唯一识别某对象,那么可以依次添加辅助属性,直到对象可以唯一识别。
●顺序标识符- 一旦在使用了强制属性和辅助属性后,对象仍然不能唯一识别,那么可以使用序数识别。有三种类型的序数识别:
图3-8 对象识别设置
图3-8 显示了WebCheckBox的强制属性和辅助属性。
小提示:这些设置是常规的全局设置,并不基于任何脚本。我们可以根据需要添加和删除。
用户定义的对象
QTP使用窗体的类名来识别对象的类型。假如我们的应用程序没有使用标准的窗体类,那么QTP就可能无法正确识别对象。Windows的搜索对话框有一些CheckBox放在了自定义的控件内,当我们试图添加他们到QTP对象库中
时,它们只能被识别为WinObject,如图3-9所示。这是由于Qtp不能把这些Ch eckBox识别成一般的测试对象。
图3-9 搜索窗口中的CheckBox识别成了WinObject 因此我们需要在QTP设置中,把这个CheckBox映射成WinCheckBox.打开Tools->Object Identification ,然后选择标准Windows环境,点击User De fined按钮,就会弹出映射对话框。点击手型按钮,然后点击CheckBox后,类名就被添加,并且我们可以映射到CheckBox,如图3-10所示。点击Add按钮添加这个映射。
映射后,QTP便可识别这个对象为WinCheckBox,如图3-11所示。
图3-11 用户自定义对象识别为Checkbox
对象库的不足
作者认为QTP 8.x的对象库有一些不足:
●对象库管理器不允许批量更新对象属性到脚本。
●其他对象下面的对象不能被删除或复制。
●当一个框体(frame)被加入到了被测的应用程序中时,那么整个测试脚本都要重新录制。
●当重新录制一个页面或者窗体时,完全一样的对象经常会重复添加,因此,会创建很多相同的页面或窗体:Page_1, Page_2诸如此类。有时候这个问题可以通过更改Web设置来解决,
打开Tools->Options…Web(Tab)->Page/Frame Options…然后更改设置如图3-12所示
图3-12页面和框体选项
小提示:以上所述的大多数的不足在QTP9.x中已经得到解决。
第四部分
第九章库函数文件
库函数文件是包含VBScript脚本的纯文本格式文件,用来声明方法,变量,类等。库函数文件可以用任意后缀名,最常用的是VBS或者TXT。库函数文件可以用来组织存放不同功能的代码。它提供了在不同的QTP脚本中分享代码的方法。下面两节介绍加载库函数文件的两种方法。
关联一个全局库函数文件
通过这种方式库函数文件的同一实例可以被当前测试的所有Action共享和访问。如图9-1,打开Test->Settings…->Resources (标签页),添加库函数
文件。
图9-1 Test Resource配置
小提示1:多个库函数文件加载顺序是从下到上。如果有两个库函数文件包含相同函数,那么会使用更靠近顶部的。
小提示2:如果库函数文件B依赖库函数文件A中的内容,库函数文件A应在列表中更靠近底部。
小提示3:QTP使用全路径名存放库函数文件。作为推荐选项,我们可以使用文件相对路径,如"..\test.vbs"
运行时动态加载本地库函数文件
QTP提供ExecuteFile方法可以在运行时动态加载库函数文件。使用这种方法时库函数文件及其内容只能在ExecuteFile执行的那个Action中可见。下面是一些例子:
'通过绝对路径加载库函数文件
ExecuteFile "C:\Test.vbs"
'通过相对路径加载库函数文件
currentTestDir = Environment("TestDir")
vbsFilePath = currentTestDir& "\..\..\CommonLibs\Test.vbs"
ExecuteFilevbsFilePath
'从Quality Center加载库
ExecuteFile "[QC-ATTACH];;Subject\CommonLibs;;\Test.vbs"
小提示:如果ActionA和ActionB都通过ExecuteFile加载了test.vbs,同时
ActionA调用ActionB,要注意他们对于所有test.vbs的变量和方法都只使用自己的副本和单独实例。
运行时动态加载全局库函数文件
如之前描述,在一个Action中直接使用ExectueFile方法只能使库函数文件在当前Action可见。但是每个QTP测试脚本可能需要一组全局库,使得对其中所有的Action都可用。取代直接在Action中使用ExecuteFile来加载库的方法,我们在某个全局库中加载库函数文件,那么它将对所有Action可用。
'C: \LibLoader.vbs
Public Function ExecuteFileGlobal (ByValfileName)
ExecuteFilefileName
End Function
我们可以将以上代码保存在一个VBS文件中并跟测试关联,使得在任意Ac tion中都能通过调用ExecuteFileGlobal方法来加载文件。这样加载的文件在所有Action中都可以使用。
'在全局区域加载文件
ExecuteFileGlobal "C:\Test.vbs"
但是当多个Action反复调用ExecuteFileGlobal会使某个库函数文件加载多次,这样每次都会破坏当前库函数文件中的全局变量的状态。
这个问题可用使用下面的方法解决。我们给ExecuteFileGlobal方法增加一个加载标记,当它为False时库函数文件就不会加载。可以通过给所有使用Execut eFileGlobal方法加载的库函数文件创建一个全局字典来实现。库函数文件的路径用来判断这个库是否被加载过。
'C: \LoadLibrary.vbs
Dim loadedFiles
Set loadedFiles = CreateObject("Scripting.Dictionary")
https://www.wendangku.net/doc/3a1000673.html,pareMode = vbTextCompare
'ExecuteFileGlobal方法动态加载文件
'Inputs - strFile: 需加载的完整文件名
' reload: 是否重新加载已载入的文件
Public Function ExecuteFileGlobal (ByValstrFile,ByValreLoad)
'判断reload为False ,之后检查文件是否加载过
If reload = False and loadedFiles.Exists (strFile) then
ExecuteFileGlobal = False
Exit Function
End if
'加载库函数文件
ExecuteFilestrFile
'将文件加入字典列表
loadedFiles (strFile) = True
ExecuteFileGlobal = True
End Function
通过一个例子可以更好理解上面代码。首先我们创建一个要动态加载的库函数文件。
'C:\TestA.vbs
Dim X
X = 2
以下代码演示ExecuteFileGlobal的使用方法
'在全局区域加载testa.vbs
ExecuteFileGlobal "C:\testa.vbs", False
Msgbox X '显示2
X = X + 2
'在全局区域加载testa.vbs,如果已经加载则忽略
ExecuteFileGlobal "C:\testa.vbs", False
Msgbox X '显示4
''在全局区域重新加载testa.vbs
ExecuteFileGlobal "C:\testa.vbs", True
Msgbox X '显示2
问题9-1. 如何动态定义全局变量
有时我们需要动态在两个或多个Action中共享变量值。使用加载库函数文件的相同概念,我们可以在运行时创建全局变量。
Declare.vbs
'C: \Declare.vbs
Sub ExecuteGlobalCode (sStatement)
ExecuteGlobalsStatement
End Sub
Action1:
'在全局范围执行代码
ExecuteGlobalCode "Dim strText"
strText = "TarunLalwani"
Action2:
'将显示"TarunLalwani"
MsgBoxstrText
理解执行作用域
理解测试脚本中全局作用域和本地作用域的区别是很重要的。
●全局作用域是QTP加载所有测试资源和场景恢复库所在。测试脚本中的
所有Action都可以访问
●本地作用域是指所有在Action中定义的作用区域,不可被此Action之外
所访问
图9-2描述了包含两个Action的QTP脚本的作用域视图,来表述QTP如何工作。
●当脚本启动时QTP创建全局作用域
●QTP首先添加所有场景恢复库。按照关联顺序从上到下
●场景恢复库之后,QTP加载所有关联在Test->Settings…->Resource
(标签)下的文件。加载的顺序是从下到上
●之后QTP根据测试定义的工作流程按顺序调用每个Action。针对每个A
ction,QTP创建一个私有的本地作用域。此作用域在每个Action周期建立和释放
●在Action1中定义的方法不会被其他Action或者全局作用域访问到
图9-2 QTP运行作用域:本地和全局作用域
小提示:如果多个全局库中有同名方法,那么最后一个加载的那个库中的方法会被调用。
Option Explicit的适用性
在库函数文件头部的"Option Explicit"声明允许程序员对所有使用的变量做强制变量声明。但我们可以看到即便在某些关联的库中有"Option Explicit"声明,对于没有声明的变量也没有抛出错误。
这个出现的原因是源于全局作用域建立的机制。我们需要在所有全局作用域中而不能只在个别全局库函数文件中使用"Option Explicit"。强制变量声明"Opti on Explicit"需要放在所有使用的库函数文件中,如果有遗漏,QTP就不会在全局域强制使用变量声明。
全局作用域内执行本地作用域代码
我们之前看过如何在本地作用域加载库函数文件("运行时动态加载本地库函数文件")以及如何从本地加载全局作用域库函数文件("运行时动态加载全局库函数文件")。
有时为了维护脚本我们可能需要在Action之前或之后增加代码。这种维护可能涉及多个数量的脚本。而QTP只允许一次打开一个脚本,所以编辑多个脚本是相当费时的工作。为了避免这种情形我们可以在所有Action中调用两个特殊的方法,代码如下:
'反射任何需要在Action之前执行的代码为语句并执行
Execute GetActionStart()
'Action相关的代码
'执行任何需要在Action之后执行的代码
Execute GetActionEnd()
GetActionStart和GetActionEnd方法返回空值或者是需要执行的一段代码。
下面是这两个方法的实现。
'每个Action之前调用的方法
'这个方法允许在Action的本地作用域动态执行代码,需要按以下格式在Action开始时调用
'执行GetActionStart()
Function GetActionStart()
'缺省没有执行代码
GetActionStart = ""
'获取需要调用这个方法的Action名字
sAction = LCase(Environment("ActionName"))
If InStr(sAction,"main") Then
GetActionStart = "ExecuteFilePathFinder.Locate(""Workaround.vbs"")"
End If
End Function
'每个Action最后调用的方法
'这个方法允许在Action的本地作用域动态执行代码,需要按以下格式在Action结束时调用
'执行GetActionEnd()
Function GetActionEnd()
'缺省没有执行代码
GetActionEnd = ""
''获取需要调用这个方法的Action名
sAction = Environment("ActionName")
''获取需要调用这个方法的Action名
sAction = LCase(Environment("ActionName"))
。。。
If InStr(sAction,"main") Then
GetActionEnd = "ExecuteFilePathFinder.Locate(""Workaround.vbs"")"
End If
。。。
End Function
第五部分
微软Windows应用编程接口(API)为开发Windows应用程序提供了很多构建好的模块。它提供各种操作方法,比如获取鼠标坐标,窗口句柄,颜色等。QTP 支持调用动态链接库内定义的这些方法,但是由于VBScript的限制,只有有限的一部分API可以在QTP中使用。
本章使用的API的更多细节和信息可以从MSDN或者Visual Studio的AP
I Viewer工具查询获得。
Extern对象
QTP提供一个Extern功能对象用来声明和调用API。
语法
Extern.Declare (RetType, MethodName, LibName, Alias [, ArgType(s)])
更多细节可参考QTP用户手册。
VB API定义语法
Private Declare Function GetForegroundWindow Lib "user32.dll" () As Long
对以上API,我们需要使用QTP的Declare方法确定使用的合适参数:
RetType = micLong(函数返回类型)
MethodName = "GetForegroudWindow" (我们可以使用任何其他名字,但是比较好的做法是使用实际API的名字)
LibName = "user32.dll" (如果不是使用Windows系统自带的DLL,那么文件名必须使用绝对路径,例如"C:\MyApp\Lib\mylib.dll")
Alias = "" 或者"GetForegroudWindow"(如果MethodName跟Alias相同,则Alias可以为空值。
ArgType(s) = 本例中不需要传入参数)。
QTP API定义
Extern.Declare micLong,"GetForegroundWindow","user32.dll","GetForegroundWindow"
以下用一些用例来演示使用API解决通常遇到的问题。
问题17-1.如何判断当前桌面最上面的为浏览器窗口
'声明GetForeGroundWindow API
extern.Declare micLong,"GetForegroundWindow","user32.dll","GetForegroundWindow"
'获取最前面窗口的句柄
hwnd = extern.GetForegroundWindow()
'判断是否有包含此句柄的浏览器窗口
isBrowser = Browser("hwnd:=" &hwnd).Exist()
If isBrowser then
Msgbox "The top most window is a browser"