测试对象模型是一大组对象类型或类,比如WebElement、WebList,QTP用这些对象类型和类来表示Web页面中的对象。每个测试对象类都有一个可以唯一标识对象的属性列表以及可以录制的方法,这个属性列表和方法列表是可以设置的,见图:
在Object Identification对话框中设置的属性录制完后都可以在Object Repository中查看。属性的具体分类见下一小节。
QTP里的对象分两个概念,一个是Test Object(简称TO)测试对象,一个是Runtime Object(简称RO)运行时对象。
这两个概念从字面上不大好理解,也容易混淆。
但从实际作用上来看,应该说TO就是是仓库文件里定义的仓库对象,RO是被测试软件的实际对象。
QTP识别对象,一般是要求先在对象仓库文件里定义仓库对象,里面存有实际对象的特征属性的值。
然后在运行的时候,QTP会根据脚本里的对象名字,在对象仓库里找到对应的仓库对象,
接着根据仓库对象的特征属性描述,在被测试软件里搜索找到相匹配的实际对象,最后就可以对实际对象进行操作了。
仓库对象TO一般在录制/编写脚本时加入仓库文件,它不仅可以在录制编写时进行修改,
也可以在运行过程中进行动态修改,以匹配实际对象。
和TO、RO相关的几个函数有:
GetTOProperty():取得仓库对象的某个属性的值
GetTOProperties():取得仓库对象的所有属性的值
SetTOProperty():设置仓库对象的某个属性的值
GetROProperty():取得实际对象的某个属性的值
理解了TO的含义,你就可以自由的用SetTOProperty()定义TO,以灵活的操作RO
比如有个测试任务,窗口上有很多待检查的记录,每条记录右边都有一个Check按钮,用来检查各条记录。
记录个数不定,所以Check按钮个数也就不定,只有一个Edit显示记录个数。
我们要对每条记录进行检查,也就是要点击每个Check按钮。
但是Check按钮个数不定,不好录制,而且个数可能也很多(上百个),即使能一一录制,那也很麻烦。
那我有一个好办法,只录制一个按钮对象,它设有两个特征属性 label=OK, index=0
然后用下面的脚本,就可以完成测试
buttonNum = CInt(JavaWindow("Test").JavaEdit("Record Num").GetROProperty("value"))
For buttonIndex = 0 to buttonNum - 1
JavaWindow("Test").JavaButton("Check").SetTOProperty("index", buttonIndex)
JavaWindow("Test").JavaButton("Check").Click
Next
或者窗口上有New、Modify、Delete、Check等好几个按钮,要把这几个按钮一一按过去
我在对象仓库里只设置一个按钮对象AnyButton,label特征属性值填任意值,然后用下面脚本执行测试
JavaWindow("Test").JavaButton("AnyButton").SetTOProperty("label", "New")
JavaWindow("Test").JavaButton("AnyButton").Click
JavaWindow("Test").JavaButton("AnyButton").SetTOProperty("label", "Modify")
JavaWindow("Test").JavaButton("AnyButton").Click
JavaWindow("Test").JavaButton("AnyButton").SetTOProperty("label", "Delete")
JavaWindow("Test").JavaButton("AnyButton").Click
JavaWindow("Test").JavaButton("AnyButton").SetTOProperty("label", "Check")
JavaWindow("Test").JavaButton("AnyButton").Click
脚本描述定义访问对象
如果Web对象没有被添加到对象库中,或者通过该Web对象的标识QTP无法识别,即对象库中没有该对象,那么可以用另一种方式:在脚本中用描述性编程来定义访问对象
常规语法为:
TestObject("PropertyName1:=PropertyValue1","PropertyName2:=PropertyValue2"...)+具体操作
如
Dim buttonIndex
buttonIndex = 1
JavaWindow("Test").JavaButton("label:=check","index:="+CStr(buttonIndex)).Click
属性详解(转载)
QTP通过三类属性来识别对象:
a)Mandatory;
b)Assistive;
c) Ordinal identifiers。
大部分情况下,通过对象的一些特定属性值就可以识别对象(类型a)。这些属性可以通过Tools->Object Identification 定义。每个测试对象类拥有一系列用于唯一确定对象的属性,这就是在这个Object Identification里查看。比如我看了一下WebEdit空间的识别方式,发现它就是通过type,name和html tag来识别。
在这里列出了所有QTP能识别的控件,以及控件的识别方式。你可以给他添加X、Y坐标进行识别。或更明显的,列表中的信息,不按名称识别,而是按ID识别。这个修改可以解决一些问题,想动手试试,但是不知道从何下手……
1. Smart Identification:
智能识别机制主要工作于测试脚本运行时(对象允许智能识别为真),当对象 库中对象的强制属性(或辅助属性)与被测应用程序中对应对象的属性不一致时,智能识别机制将会启动。其主要原理为:先选择某个基本属性进行比较,若对象多 于一个,再继续添加属性筛选;若添加的对象属性造成无对象匹配,则淘汰该属性,应用该方法直至找到唯一对象并对该对象执行操作。(若所有属性的添加或淘汰 都无法识别唯一对象,QTP将应用ordinal identifier去识别对象。)
以上是原话,然后我的理解是:比如要识别页面上的一个webedit控 件,然后这个空间有那么多的属性,qtp会根据他的属性,然后从objectidentification里的强制属性和辅助属性里找,假如一致,就可以 识别到他就是identification机制里定义的webedit了,假如还是识别不到,就用智能识别。再不行,就用index。
在界面中可看到各种标准Windows控件对应的对象识别方法,例如,对 于Dialog控件,使用的是“is child window”、“is ownedwindow”、“nativeclass”和“text”这四个控件对象的属性来区别出一个唯一的Dilalog控件对象。可以单击 “Add/Remove”按钮,选择更多的控件属性来唯一识别控件。
以下这篇帖子有部分qtp官方文档的原话,我觉得理解起来更加权威些:
When you run a test, QuickTest searches for the object that matches the
description it learned (without the ordinal identifier). If it cannot find any
object that matches the description, or if it finds more than one object that
matches, QuickTest uses the Smart Identification mechanism (if enabled) to
identify the object. In many cases, a Smart Identification definition can help
QuickTest identify an object, if it is present, even when the learned
description fails due to changes in one or more property values. The test
object description is used together with the ordinal identifier only in cases
where the Smart Identification mechanism does not succeed in narrowing
down the object candidates to a single object.
The Object Identification dialog box also enables you to configure new
user-defined classes and map them to an existing test object class so that
QuickTest can recognize objects from your user-defined classes when you
run your test.
智能识别可是在三个层面设置可是启用:
a. 类,即Object Identification对话框中
b. 对象,即Object Repository对话框中
c. 测试中,即Run Testing对话框中
2. Ordinal Identifier:
QTP除了可以获取到被测对象的主属性、辅助属性值外,还可以获取到 被测对象的Ordinal Identifier值。当QTP发现有多个对象具有相同的主属性值、辅助属性值而无法对它们进行唯一识别时, Ordinal Identifier会获取每个对象的序列值,以将它们区别开来。
由于序列值是一个相对值,任何页面的变更都有可能导致这些值发生改变,因此,只在主属性与辅助属性无法唯一识别对象的情况下,QTP才会获取该序列值。
在运行测试脚本时,如果使用对象的属性值以及Smart Identification机制都无法唯一识别应用程序中的对象,才会使用到序列值。如果QTP可以通过其它属性值对对象进行识别,则会忽略序列值。
QTP可以使用以下类型的ordinal identifiers来识别对象:
Index 表示对象在程序代码中的出现顺序,这个顺序是相对于其它具有相同属性的对象而言的。
Location 表示对象在窗口、Frame或对话框中出现的顺序,这个顺序是相对于其它具有相同属性的对象而言的。
CreationTime(仅适用于Browser对象) 表示Browser对象打开的顺序,这相顺序是相对于其它已打开的具有相同属性的对象而言的。
一般情况下,Ordinal Idenfifier类型适用于所有类。在Object Identification窗口,通过Ordial identifier下拉框,可以选择其它类型。
注:QTP在录制脚本时,如果通过主属性与辅助属性已能够唯一识别对 象,则不会获取对象的ordinal identifier顺序值。你可以在脚本录制完成后,在Object Properties或Object Repository对话框中使用Add/Remove操作,手动添加顺序值。
QTP中设置共享对象库(转载)
很多时候我们总是觉得管理QTP的脚本比较烦.因为除了要对代码的管理之外,还要保证QTP对象库的完整.每一个用例脚本的生成,同时又都会生成一个对象库.那么是不是可以做一个公共的对象库来给各个用例脚本调用呢?接下来,我们就来实现共享对象库的应用.
第一步:把需要加到共享对象库中的各个用例脚本的对象库,分别导出成.tsr文件.
操作方法:先用QTP打开已经录制完毕的脚本后,选择Resources--> Object Repository .
然后file-->export Local Objects导出tsr文件
第二步:把需要加入到共享对象库中的各个用例脚本的对象库,合并对象及对象属性,形成一个大的共享对象库.(可选)
操作 方法:打开Resources-->Object Repository Manager.找到Tools菜单,然后选择打开ObjectRepository mergetool.选择两个要合并的对象库文件,进行对象合并.一次只能合并两个.所以如果这里你要合并多个对象库文件的话,先合并两个后,保存成一个 新的.tsr文件.然后再重复上面的操作,选择这个新的.tsr文件和另一个准备好的对象库合并.合并的时候,你会发现,在两个对象库文件中,相同的对象 合并成一个,不同的对象,全部被完整增加进去.然后形成一个大的对象库.现在只要保存这个共享对象库.如果以后有新增的对象,可以重复上述操作.
第三步:调用上面保存好的共享对象库.给新的脚本使用.
在任意test中,都可以依照操作步骤:Resources-->Associate Repository.选择上面保存好的共享对象库的.tsr文件.加入到Associate Repository中.
完成这一步后,其实整个共享对象库的操作已经完成.你在对象库中可以看到以后的对象.在录制新脚本的时候,如果对象已经存在,就不会再被记录,只有在这个对象库中没有的对象才会被记录进去.回放脚本,对象识别成功.
二、QTP操作对象的原理
QTP为用户提供了两种操作对象的接口,一种就是对象的封装接口,另一种是对象的自身接口。
对象的自身接口是对象控件本身的接口,只要做过软件开发,使用过控件的人应该很清楚。
对象的封装接口是QTP为对象封装的另一层接口,它是QTP通过调用对象的自身接口来实现的。
两种接口的脚本书写格式的差别在于:
自身接口需要在对象名后面加object再加属性名或方法名,
封装接口就不用在对象名后面加object。
具体格式如下:
对实际对象的操作:
对象.object.自身属性
对象.object.自身方法()
对象.GetROProperty("封装属性")
对象.封装方法()
对仓库对象的操作:
对象.GetTOProperty("封装属性")
对象.GetTOProperties() ’获取所有封装属性的值
对象.SetTOProperty("封装属性", "封装属性值")
比如操作JavaEdit对象,通过QTP封装的封装接口,脚本如下:
设置JavaEdit的内容:
JavaDialog("Add NE").JavaEdit("NE Name").Set "NE1"
读取JavaEdit的内容: msgbox JavaDialog("Add NE").JavaEdit("NE Name").GetROProperty("value")
如果通过JavaEdit的自身接口,脚本如下:
设置JavaEdit的内容:
JavaDialog("Add NE").JavaEdit("NE Name").object.setText("NE1")
读取JavaEdit的内容:
Msgbox JavaDialog("Add NE").JavaEdit("NE Name").object.getText()
QTP执行JavaEdit().Set语句时,是通过执行JavaEdit().object.setText()来实现的。
QTP执行JavaEdit().GetROProperty("value"),是通过执行JavaEdit().object.getText()来实现的。
JavaEdit对象的封装接口Set()和GetROProperty("value"),是QTP封装JavaEdit对象的自身接口setText()和getText()而得来的。
对象的封装接口是QTP使用的缺省接口,我们录制出来的脚本都是使用封装接口,大家用的也都是封装接口。
但是封装接口不如自身接口丰富,因为QTP只是封装了部分常用的自身接口嘛。
所以我们在需要时,可以绕过封装接口,直接调用对象的自身接口。
不过有些自身接口不够稳定,在实践中偶尔会出现问题,但是概率很少。
封装接口有相应功能的话,就尽量用封装接口吧!
理解了封装接口和自身接口的原理,我们就可以更加灵活的操作对象了。
但是我们怎么知道对象都有哪些封装接口和自身接口呢?
其实很简单,用对象查看器(Object Spy)查看对象,在查看窗口里有列出这些接口,包括属性和方法。
窗口中间有选择栏让你选择Run-time Object或者Test Object,
当你选择Runtime Object时,它显示的就是对象的自身接口(自身的属性和方法)
当你选择Test Object时,它显示的就是对象的封装接口(封装的属性和方法)
(注意:GetROProperty访问的是实际对象的封装接口,GetTOProperty访问的是仓库对象的封装接口,
两者访问的都是对象的封装接口,即Object Spy窗口里选Test Object时显
示的属性。
不要以为GetROProperty访问的是自身接口,即Object Spy窗口里选Run-time Object时显示的属性。
QTP里的Test Object/Run-time Object的概念太容易让人混淆了!
它既用来区分仓库对象和实际对象,又用来区分对象的封装接口和自身接口。)
明白了这些,你还等什么呢?快拿起对象查看器,看看对象都有哪些封装接口和自身接口,肆意的操作它,玩弄它吧!
比如执行
JavaDialog("Add NE").JavaEdit("NE Name").object.setVisible(false)
哈哈,你的JavaEdit对象就当场消失不见了!!!
你可以拿这个做恶作剧,指着这个窗口逼问开发人员,JavaEdit对象哪去了?
开发人员肯定瞪大眼睛看着这个窗口,当场翘掉!!!