模型对象及属性

模型管理器中的树形结构中的每个节点是一个模型对象,是匹配和操作控件的一个自动化对象,该对象通过自身的识别属性匹配被自动化应用中的目标控件,并通过自身的API————对象操作API来操作目标控件。

因此了解对象识别属性有助于我们弄清楚不同情况下该使用哪种识别属性来达到最佳的匹配效果。

注意,这里的对象属性通常只指识别属性,但是你会在模型管理器中的控件操作标签页中看到属性这个字眼。但实际上这些属性的本质都是对象操作API,仅仅是API操作的一种分类,具体的可以查看对象操作API一节。 识别属性

识别属性概述

前面提到了在操作控件时,实际上是调用了模型对象的操作API,接着实例对象根据自身的识别属性去匹配应用中的目标控件。因此,不合理的识别属性可能会导致:

  1. 匹配不到目标控件:提示错误“1003:不能找到这个对象”;
  2. 匹配到多个目标控件:模型管理器右上角提示找到多个控件,左下角列出所有控件的位置信息;

对于前一种情况,在运行中会导致报错,是必须要解决的;而后一种情况,尽量通过设置合理的识别属性来避免。

识别属性的注意点

  • 侦测器识别添加控件时,自动推荐了一些识别属性。将控件的部分属性作为基本的识别属性,比如:typenameautomationId等,除了必需的type识别属性外,生成哪些识别属性取决于目标控件的类型和拥有哪些属性。

  • 识别属性根据控件被添加进模型管理器时的状态创建。因此如果控件某些属性是动态生成的,则不应该将它们作为识别属性。比如为了得到计算器的结果,需要识别显示计算结果的Text控件,如下图。这时使用它的name属性就变得十分不可靠,因为通过观察会发现name属性会随着内容一起更改,因此还要手动将name属性删除以保证能够匹配到结果所在的控件。

    计算器结果

    显示内容更改后无法找到目标控件

  • 识别属性运行时属性的关系。

    • 对象有识别属性,从之前的阅读可以了解就是用于匹配控件,是存储在模型文件中,或代码中定义(描述模式)。此外,为了提高识别能力,模型管理器额外提供了一些辅助识别属性,比如indexlevelToParent等。
    • 运行时属性是自动化过程中从控件获取的属性值。而根据不同的控件类型,也提供了不同的API获取这些属性。例如hwnd返回控件窗口的句柄, xy返回控件的所在位置等。
    • 它们两者之间可能有一部分是相同。但运行时属性可能随着时间被应用自身所改变。

首字母大写 or 驼峰式命名:出于可读性上的考虑,在模型管理器中,识别属性采用了首字母大写的命名方式;但在实际代码中,属性名仍然是使用驼峰式命名的,下面的介绍也将采用这种命名,之后会介绍原因。

标识属性和其它属性?

识别属性标签页中可以看到属性又被分为标识属性其它属性,可以将前者理解为生效的识别属性,后者只显示而不生效,可以使用添加属性按钮添加到标识属性中。

识别属性及分类

在侦测器侦测控件时,会取得控件所有可用的识别属性,并且根据当前控件的属性赋予合适的默认值。在模型管理器中也可以管理控件的的识别属性,还可以手动编辑识别属性的值来更好的匹配控件。

一. 通用识别属性

通用识别属性指的是所有控件都可以有的识别属性,与控件类型无关。

name

控件的名称,通常与value属性的值相同。

type

控件的类型,必需的属性,无法更改和删除,影响控件提供的操作API。类型比如Button、Text、Window、List、Table、Tree等等。

className

控件所属的控件类,来自于控件在代码中的类,比如Qt中通常用作窗口的控件是QWidget,那么应用中的窗口的type为Window,className则为QWidget,如下图:
Window控件的className
由于className的值通常只与type和应用使用的框架(Qt、WPF、WinForm等等)相关,所以有时候可以通过className属性来分辨应用所用的框架。

automationId

一般来自.NET框架,是目标控件中用于自动化测试的一个属性,在官方文档中的解释为:

An ID for an element that is unique among siblings within its container.
相对于同一个容器中的兄弟姐妹们来说控件唯一的标识。

因此适用于.NET框架(比如WPF、WinForm等)应用和控件的自动化,对于其它类型的应用中的控件而言一般都是空值。

accessKey

目标控件的AccessKeyPropertyId,与automationId类似,都是用于自动化的一个属性,同样常常是空值。

boundingRectangle

目标控件的形状信息,由一组长度为4的数组描述,数组中的值代表:[x, y, width, height],其中的xy分别代表控件在桌面坐标系中的横纵坐标,widthheight代表控件的宽度和高度。
boundingRectangle通常用于绘制控件的高亮框,由于应用窗口化启动时的大小和位置都不确定,boundingRectangle就会十分不稳定,因此也极少作为识别属性使用。如果应用中的控件无法被很好的识别,必须要使用控件的坐标等信息作为操作目标,那么推荐使用虚拟控件作为解决方案。

二. 辅助识别属性

index

目标控件在被一次匹配到的多个控件中的位置,也是当识别属性过少时的解决方案之一。这里需要解释一下模型管理器在识别到多个控件时的处理方法:

识别到多个控件时:当模型管理器/侦测器识别到多个控件时(可能是由于识别属性比较笼统,或者应用中存在同名控件等等),会以数组的形式将识别到的所有控件返回,同时在模型对象树中无法匹配唯一控件的对象节点会被标记会红色。index属性用于取出数组中指定位置的控件,比如为0时返回第一个,为1时返回第二个…并且将识别到的所有控件信息显示在模型管理器左下方。

当模型管理器发现对象节点会匹配到多个控件时,会将该对象节点标记会红色,这称作唯一性验证表示该节点可能会引起不稳定的结果,可以使用自动添加索引或者由自己手动添加索引来解决。

index属性适用的场景非常广,但是也有其局限性。前文提到,index属性是根据识别到多个控件时返回的数组中的定位。对于控件布局不会变化的应用来说是可以的,但是如果某个应用它的控件布局、控件数量可能会变化,比如Qt自动化中针对List和Tree控件的自动化,index可能不可靠。可以查看演练:操作Qt应用中的QListView演练:操作Qt应用中的树——TreeView了解更多。

即使没有识别到多个控件,index属性为0也能生效;但是如果使用其它的值只会返回错误1003:不能找到这个对象

objectType

目标控件的抽象类型。由于控件的类型type、所属的控件类classType都是不可改变的,在开发完成的时候就确定了,而objectType是告诉CukeTest该控件应该作为什么类型的控件来处理而设计的。

比如表格控件Table中的行,没有实际对应的控件类型,只是一个空属性的容器控件,但是CukeTest会在识别时将其objectType设为TableRow,从而会被当作TableRow控件使用,可以调用和调试TableRow控件的操作和属性方法。并且将调用代码复制到脚本编辑器中时,脚本会呈现如下:

  1. await model.getTableRow("TableRow"); // 有控件相关的智能提示

在上面的脚本中,会出现方法的智能提示;而在没有使用objectType属性的较旧的CukeTest版本中复制的脚本则不会有提示:

  1. await mdoel.getGeneric("TableRow"); // 只有通用方法的提示

childLocator

目标控件包含的子控件。当两个容器拥有完全一致的属性时,也可以通过目标容器是否包含某个子控件作为识别属性,这个属性可以通过模型管理中的直接添加,并且可以直接通过下拉框选中已有的子控件,如下图: 通过下拉框选择ChildLocator

levelToParent

目标控件的所处层次。对于多层次的应用来说,有时会出现目标控件存在同名的子控件或父控件,这个时候通过限制识别层级可以避免识别到不需要的控件,如下图所示:
levelToParent属性识别示意图

三. 特有属性

特有属性是指只存在于某些特定类型控件的属性。

text

目标控件的文本内容,存在于Text控件中。

url

目标控件指向的链接,存在于Hyperlink控件中。

title

目标控件的标题,通常存在于Window控件中。

hWnd

目标控件的窗口句柄,只存在于Window控件中。

helpText

目标控件的帮助文字,大部分控件都有该属性,但常常为空。

processId

目标控件所处应用的进程标识,也就是PID,在描述模式中比较常用到。

在代码中使用识别属性

在一些情况下,我们可以跳过模型管理器直接在代码中通过识别属性来取得目标控件,这在很多情况下可以完成更加灵活的自动化,就像Qt的几个演练一样。而使用的就是getControls方法,关于它的用法说明可以点击查看获取对象API