如何识别和访问对象

测试人员在从零开始编写脚本(或修改录制脚本)时面临的最重要问题之一是如何访问用户界面中的对象。我们可以通过使用Object waitForObject(objectOrName)函数来获取对象的引用。此函数等待对象变为可见且可用,然后返回对其的引用,或者在超时时引发可捕获的异常。如果需要访问不可见对象的引用,我们必须使用Object findObject(objectName)函数,该函数不等待。这两个函数都接受对象名称,但获取正确的名称可能很棘手,因此在我们进入Squish特定版本和脚本语言特定细节之前,我们将在此处解释问题和解决方案。

Squish支持几种完全不同的命名方案,包括符号名真实名(也称为多重属性名)、限定名层次结构名。在Squish的大多数版本中,当录制脚本时使用符号名。在Squish for Tk和Squish for Web中,也可以使用限定名和层次结构名。对于手写的代码,您可以使用符号名或真实名。最好使用符号名,尽管在某些用途下真实名更方便。

在无法通过名称识别对象的情况下,可以基于子图像进行纯粹视觉的搜索。

如何访问命名对象

最简单的情况是,当程序员为应用程序对象分配了一个显式的名称。例如,使用Qt工具包,对象可以像这样命名

    cashWidget->setObjectName("CashWidget");

以这种方式给对象命名时,我们可以使用仅指定两个属性的真正名称来识别它:类型和名称。根据所使用的对象映射实现,访问cashWidget标签的语法略有不同。

以下是在使用Object waitForObject(objectOrName)函数时在多种脚本语言中提供的示例,而当使用基于文本的对象映射

cashWidget = waitForObject("{name='CashWidget' type='QLabel'}")
var cashWidget = waitForObject("{name='CashWidget' type='QLabel'}");
my $cashWidget = waitForObject("{name='CashWidget' type='QLabel'}");
cashWidget = waitForObject("{name='CashWidget' type='QLabel'}")
set cashWidget [waitForObject "{name='CashWidget' type='QLabel'}"]

要创建表示真实(多重属性)名称的字符串,我们创建一个包含开括号、一个或多个由空格分隔的属性项(每个属性项具有propertyname='value'的形式)和关闭括号。

基于脚本的对象映射中使用时,可以将字典传递给Object waitForObject(objectOrName)函数

    cashWidget = waitForObject({"name": "CashWidget", "type": "QLabel"})
    var cashWidget = waitForObject({'name':'CashWidget', 'type':'QLabel'});
    my $cashWidget = waitForObject({'name'=>'CashWidget', 'type'=>'QLabel'});
    cashWidget = waitForObject({:name=>'CashWidget', :type=>'QLabel'})
    set cashWidget [waitForObject [::Squish::ObjectName name CashWidget type QLabel ]]

在基于脚本的对象映射中,构造了一个字典对象,它接受一组键值对作为匹配特定对象的属性。有关构建对象名称的可用API的更多信息,请参阅基于脚本的对象映射API

对于大多数工具包,至少必须指定两个属性,其中一个为对象的type。如果对象具有对象名称,仅使用typename属性就足够了(前提是名称在指定类型的对象中是唯一的)。

一旦我们有了对象的引用,就可以访问其属性,例如,与预期值进行比较,或对其进行更改。我们将在接下来的Squish特定版本部分中了解如何做。

有关更多信息,请参阅应用程序对象视图属性视图

如何使用真实(多重属性)名称访问对象

有些对象无法通过名称识别,在这种情况下,会有一个特殊属性,命名为 unnamed,其值为1。当我们面临名称未知的对象时,通常可以使用其他属性来识别它们。例如,下面是识别和访问一个 payButton 按钮的方法之一。

    payButtonName = {"text": "Pay", "type": "QPushButton", "visible": 1}
    payButton = waitForObject(payButtonName)
    var payButtonName = {'type':'QPushButton', 'text':'Pay', 'visible':'1'};
    var payButton = waitForObject(payButtonName);
    my $payButtonName = {'type'=>'QPushButton', 'text'=>'Pay', 'visible'=>'1'};
    my $payButton = waitForObject($payButtonName);
    payButtonName = {:type=>'QPushButton', :text=>'Pay', :visible=>'1'}
    payButton = waitForObject(payButtonName)
    set payButtonName [::Squish::ObjectName text Pay type QPushButton visible 1]
    set payButton [waitForObject $payButtonName]

上述真实名称在每个脚本语言中表示为本地的键值映射,这就是 基于脚本的对象映射 的工作方式。真实名称也可以使用以前的 基于文本的对象映射 来表示,这是 Squish 6.4 之前使用的。

payButtonName = "{type='QPushButton' text='Pay' visible='1'}"
payButton = waitForObject(payButtonName)
var payButtonName = "{type='QPushButton' text='Pay' visible='1'}";
var payButton = waitForObject(payButtonName);
my $payButtonName = "{type='QPushButton' text='Pay' visible='1'}"
my $payButton = waitForObject($payButtonName);
paybuttonName = "{type='QPushButton' text='Pay' visible='1'}"
payButton = waitForObject(payButtonName)
set payButtonName {{type='QPushButton' text='Pay' visible='1'}}
set payButton [waitForObject $payButtonName]

这些名称为这个特定的 GUI 指定了足够的条件,以至于只有一个带有 支付 文字的按钮。

在某些情况下,我们感兴趣的对象既没有名称也没有独特的文字。但即便如此,通常也可以识别它。例如,一个无名的微调框可能是相关标签的伴侣,因此我们可以利用这种关系来唯一地识别微调框。一个 基于脚本的对象映射 允许使用纯脚本变量引用其他对象名称。在这里,伴侣是通过从 对象映射 中复制的符号名称来识别的。

    paymentSpinBoxName = {"buddy": names.make_Payment_This_Payment_QLabel,
                          "type": "QSpinBox", "unnamed": 1}
    paymentSpinBox = waitForObject(paymentSpinBoxName)
    var paymentSpinBoxName = {'buddy':names.makePaymentThisPaymentQLabel,
        'type':'QSpinBox', 'unnamed':'1'};
    var paymentSpinBox = waitForObject(paymentSpinBoxName);
    my $paymentSpinBoxName = {'buddy'=>$Names::make_payment_this_payment_qlabel,
        'type'=>'QSpinBox', 'unnamed'=>'1'};
    my $paymentSpinBox = waitForObject($paymentSpinBoxName);
    paymentSpinBoxName = {:buddy=>Names::Make_Payment_This_Payment_QLabel,
        :type=>'QSpinBox', :unnamed=>'1'}
    paymentSpinBox = waitForObject(paymentSpinBoxName)
    set paymentSpinBoxName [::Squish::ObjectName type QSpinBox unnamed 1 \
        buddy $names::Make_Payment_This_Payment_QLabel ]
    set paymentSpinBox [waitForObject $paymentSpinBoxName]

如果没有任何显而易见的方式来识别一个对象,可以使用 Spy 工具 让 Squish 提供一个适当的名称,或者在测试中快速记录与感兴趣对象的交互,然后将鼠标放在符号名称上,右键点击选择 打开符号名称 来查看它在 对象映射 中的真实名称。你可以在真实测试中使用其一。

在某些情况下,我们可能想使用一个文本会变化的属性。例如,如果我们想识别窗口标题文本根据窗口内容变化的窗口。这可以通过 Squish 精进的匹配能力来实现,稍后将在 改进对象识别 中描述。

如果 waitForObject(objectOrName) 函数无法找到具有给定名称的对象,则引发异常。对于大多数语言,这是一种基类异常,但对于 Python,它是更专业的 LookupError,对于 Ruby 是 Squish::LookupError。如果未捕获异常,错误条目将添加到 Squish 的日志中,在 测试结果视图 中。请参见 如何在测试脚本中处理引发的异常。)这通常是我们的期望,因为这可能意味着我们误输入了属性值之一。但是,如果对象可能只在某些情况下存在(例如,如果选择了一个特定的选项卡),我们可以使用 Boolean object.exists(objectName) 函数来检查具有给定名称的对象是否存在,如果存在,则在那个情况下对其进行任何测试。例如,在 Python 中,我们可以编写如下代码(假设使用了 基于脚本的对象映射

moreOptionsButtonName = {'type': 'QPushButton', 'name': 'More Options'}
if object.exists(moreOptionsButtonName):
    moreOptionsButton = waitForObject(moreOptionsButtonName)
    clickButton(moreOptionsButton)

这种方法的一个优点是,如果对象不存在,脚本可以立即得知。将其与这种方法进行比较:

try:
    moreOptionsButtonName = {'type': 'QPushButton', 'name': 'More Options'}
    moreOptionsButton = waitForObject(moreOptionsButtonName)
except LookupError:
    pass # button doesn't exist so don't try to click it
else:
    clickButton(moreOptionsButton)

使用Boolean对象.exists(objectName)函数可能比使用Object waitForObject(objectOrName)函数慢,因为waitForObject函数将等待20秒(默认超时时间,可以通过第二个参数更改),尽管两种方法都是有效的。

如何使用符号名称访问对象

Squish在录制测试时使用符号名称来标识小部件。符号名称的格式取决于测试是使用基于文本的对象映射还是基于脚本的对象映射:在前者的情况下,符号名称是以冒号(:)开头的简单字符串,在后者的情况下,符号名称是脚本变量。一些符号名称很容易理解,例如,":fileNameEdit_QLineEdit",而其他符号名称可能更难以理解,例如,":CSV Table - before.csv.File_QTableWidget"——这个符号名称包含了显示当前文件名的窗口标题。符号名称是由Squish编程生成的,尽管它们也可以用于手写的代码,或者修改或使用记录的测试的片段。

与真实名称相比,符号名称的一个主要优势是:如果一个真实名称所依赖的属性发生了变化(例如,由于 kro 的变化),则真实名称将不再有效,并且需要更新所有测试脚本中的使用。但是,如果一个符号名称已被使用,则符号名称所引用的真实名称(即名称的属性及其值)可以简单地更新在对象映射中,无需更改测试。这就是为什么尽可能使用符号名称而不是真实名称几乎总是更好的原因。(见对象映射对象映射视图。)

如何使用图像访问对象

如上所述,按名称访问对象是首选方法。在涉及动态用户界面的情况下,选择合适的属性可能需要一些工作,但将产生最稳健的测试脚本。例如,考虑到应用程序GUI样式的变化。

然而,在某些情况下,无法通过属性来处理对象识别。至少不是那么容易。这种情况包括

  • 属于自定义或第三方的UI工具包的控件。
  • 2D或3D画布上的图形形状。
  • 访问主AUT之外的程序。这包括操作系统本身的菜单、任务栏和桌面控件。

为了处理这样的情况,Squish提供了一个基于子图像的替代查找机制。使用ScreenRectangle waitForImage(imageFile, [parameterMap], [searchRegion])可以在屏幕上搜索任意大小和形状的图像。确定的位将为ScreenRectangle,它可以再次作为交互函数输入,如mouseClick(screenPoint, modifierState, button)。

mouseClick(waitForImage("customcontrol.png"))

通过交互式使用记录测试用例动作()和例如插入> mouseClick([Image])动作()最方便地创建所需的子图像以及插入相应的脚本功能。

©2024 Qt公司有限公司。包含在此处的文档贡献是各自所有者的版权。
提供的文档许可根据自由软件基金会发布的GNU自由文档许可版本1.3的条款。
Qt及其相关标志是芬兰Qt公司及其它在全球的商标。所有其他商标均属于其各自的所有者。