https://segmentfault.com/a/1190000010339021

从事Windows 桌面应用自动化测试也有一些年了,现在谈这个话题并不流行。因为除了企业级应用,很少有公司会只选择Windows桌面作为目标用户平台,一般都会考虑跨平台的浏览器解决方案,桌面应用的地位渐渐下降,这是事实。

当年初入测试行业时就被外包公司看上了,在微软的圈子里一待就是4年,时间真快。不得不说,一个大学刚毕业的毛头小子看到微软里各种技术和工具真像极了刘姥姥进大观园,那时候还没有iPhone,也没有Android,微软一统天下。

本文主要介绍一下我对Windows UI自动化的一些看法以及WPATH的实现和应用,如果你还在从事Windows桌面应用的自动化测试,应该能有一些帮助。

为何发明WPATH

Windows UI 自动化,顾名思义就是在Windows平台实现软件的界面自动化,比如自动打开Excel填入一些数据,输入公式,获取结果。正经的用途就是软件自动化测试,避免重复的手工操作;不正经的用途就是写外挂,各种投机取巧的工具等等。

最简单粗暴的实现方案就是录制回放,优点很明显,简单快速;缺点也一样明显,不可靠因素太多。主要的代表就是QTP,来自HP公司,这应该是很多同学都听过的一款测试工具。

进阶的方案就是使用微软提供的自动化工具集:UI Automation。UI Automation是Microsoft .NET 3.0框架下提供的一种用于自动化测试的技术,是在MSAA基础上建立的,MSAA就是Microsoft Active Accessibility。

如果你使用过.NET 提供的UI Automation相关的类库,应该有一个直观的感受,就是非常啰嗦,举一个例子:

AutomationElement ControlTypeComboBox = grdClassBook.FindFirst(
TreeScope.Children,
new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.ComboBox)); AutomationElement cellElement = ControlTypeComboBox.FindFirst(
TreeScope.Children,
new PropertyCondition(AutomationElement.AutomationIdProperty, "ListBox"));

每当你尝试去获取一个UI元素时,都需要使用FindFirst之类的方法去查询指定的PropertyCondition,而PropertyCondition使用起来也不简单,特别是当你需要拼接多个AND或者OR多个条件时。

var btnCondition = new AndCondition(
new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Button),
new PropertyCondition(AutomationElement.NameProperty, "ok"));

才两个条件就这么多代码了?你看看搞Web自动化的同学都可以用XPATH,快速定位和查询元素 /div[@id='ok'],多好。既然我们那么羡慕XPATH,那我们就搞一个出来,让做Windows桌面自动化的同学也可以High一把。

WPATH实现原理

具体代码我就不在此赘述了,想读代码的同学可以直接移步至Github:https://github.com/tobyqin/wpath

WPATH的主要原理就是通过反射的方式去获取当前方法或者属性的Attribute,在Attribute中我们可以定义类似于XPATH的语法,我 且称之为WPATH。最后经过表达式解析转换成对应的Find方法和Condition,举一个例子说明:

[WPath("/Edit[@id='txtId' or @Class='TextBox']")]
public AutomationElement EditControl
{
get { return this.AppElement.FindByWPath(); }
}

当调用FindByWPath()时,该属性上的WPath Attribute就会被解析出来,其中的 /会被解析成FindFirst,Edit会被解析成ControlType.Edit,中括号里的条件最后被组合起来,调用的最终结果大致如下:

public AutomationElement EditControl
{
get
{
return this.AppElement.FindFirst(TreeScope.Children,
new AndCondition(
new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Edit),
new OrCondition(
new PropertyCondition(AutomationElement.AutomationId, "txtId"),
new PropertyCondition(AutomationElement.Class, "TextBox"))));
}
}

痛苦的感觉一下减轻许多,有没有?

更详细的WPATH用法

如果你要在项目中使用WPATH,可以通过nuget包安装:

PM> Install-Package WPath

简单说明

  1. WPath 和 XPath 类似,以 '/' 开头。

  2. 可以使用多个 '/' 来定位目标元素。

  3. 节点名字来自于MSDN定义好的 control type

  4. 目前WPath支持的查询属性如下:

  • Name (NameProperty)

  • ID (AutomationIdProperty)

  • Class (ClassNameProperty)

  • Enabled (IsEnabledProperty)

  • FrameworkID (FrameworkIdProperty)

举例子

/Group/Button

  • 获取第一个Group下的第一个Button。

//Button[@Name='Save']

  • 在子孙节点中获取第一个Name为 "Save" 的元素。

/[@Name='TabContainer']/Button[2]

  • 获取Name为 "TabContainer"的控件下的第二个Button,注意,控件类型名称可以为空。

/Button[@ID='AddButton' and @Name='Add']

  • 获取一个automation ID 为 'AddButton'  name 为 'Add' 的Button。

/Button[@ID='AddButton' or @Name='Add']

  • 获取一个automation ID 为 'AddButton'  name 为'Add'的Button。

/Button[first()]

  • 获取当前元素下第一个Button。

/Button[last()]

  • 获取当前元素下最后一个Button。

实际运用

推荐使用Attribute的方式进行调用,可用于类方法或者属性。

[WPath("/Edit[@id='txtId' or @Class='TextBox']")]
public AutomationElement EditControl
{
get { return this.AppElement.FindByWPath(); }
} [WPath("/Button[first()]")]
public AutomationElement GetFirstButton()
{
return this.AppElement.FindByWPath();
}

或者直接调用 FindByWPath(path) 来定位目标元素。

var path = "/Edit[3]";
var e = this.AppElement.FindByWPath(path);
Assert.AreEqual("txtKey", e.Current.AutomationId);
Assert.AreEqual(ControlType.Edit, e.Current.ControlType); path = "/Button[@name='OK']/Text[1]";
e = this.AppElement.FindByWPath(path);
Assert.AreEqual("OK", e.Current.Name);
Assert.AreEqual(ControlType.Text, e.Current.ControlType);

小贴士

  • 元素类型节点是大小写不敏感的,比如:

    • @name = @Name

    • /edit = /Edit

  • 父节点定位 ../ 目前不支持,因为有点复杂。

更多的说明建议还是去看Github中的说明文档,或者直接看单元测试

后记

Windows UI 自动化的坑还是挺深的,填坑的人也不少,我推荐有需要的同学去学习和了解一下 White。White是一个非常好UI Automation 封装框架,相信我,能省下你不少时间。

Windows UI自动化测试的XPATH实现 - WPATH的更多相关文章

  1. 从UI Automation看Windows平台自动化测试原理

    前言 楼主在2013年初研究Android自动化测试的时候,就分享了几篇文章 Android ViewTree and DecorView Android自动化追本溯源系列(1): 获取页面元素 An ...

  2. 对Windows桌面应用程序进行UI自动化测试

    题记:本文简述如何利用appium对Windows桌面应用程序进行UI自动化测试. 所谓UI自动化测试,就是模拟一个用户,对应用程序的UI进行操作,以完成特定场景的功能性集成测试. 要对Windows ...

  3. 基于Appium的UI自动化测试

    为什么需要UI自动化测试 移动端APP是一个复杂的系统,不同功能之间耦合性很强,很难仅通过单元测试保障整体功能.UI测试是移动应用开发中重要的一环,但是执行速度较慢,有很多重复工作量,为了减少这些工作 ...

  4. 使用WatiN进行UI自动化测试

    Watin是一个UI自动化测试工具,支持ie/firefox,官方网站:http://watin.org/. 主要有以下特点: 支持主要的html元素,见:http://watin.org/docum ...

  5. 【转】Web UI自动化测试原理

    目前市面上有很多Web UI自动化测试框架,比如WatiN, Selinimu,WebDriver,还有VS2010中的Coded UI等等.  这些框架都可以操作Web中的控件,模拟用户输入,点击等 ...

  6. UI自动化测试(三)对页面中定位到的元素对象做相应操作

    前两天分别讲述了UI自动化测试基础以及对页面元素该如何进行定位,这一篇自然就是对定位到的页面元素对象进行相应操作啦. 阅读目录 1.常用操作元素对象的方法 2.鼠标事件操作 3.键盘事件操作 4.We ...

  7. <自动化测试方案_7>第七章、PC端UI自动化测试

    第七章.PC端UI自动化测试 UI自动化测试又分为:Web自动化测试,App自动化测试.微信小程序.微信公众号UI层的自动化测试工具非常多,比较主流的是UFT(QTP),Robot Framework ...

  8. [原创]浅谈Web UI自动化测试

    [原创]浅谈Web UI自动化测试 Web UI自动化测试相信大家都不陌生,今天来谈谈这个,我最早接触自动化测试时大约是在2004年,2006年当时在腾讯财付通算是开始正式接触自动化测试,之所以是正式 ...

  9. 浅谈UI自动化测试

    最近一直在学习python,正好部门技术结构调整,就开始了点工向UI自动化测试的转变,我要说瞌睡来了就掉枕头么? 不过还好,可以将python的学习成果在自动化测试中实践... 1.about自动化测 ...

随机推荐

  1. ReentrantLock 的公平锁源码分析

    ReentrantLock 源码分析   以公平锁源码解析为例: 1:数据结构: 维护Sync 对象的引用:   private final Sync sync; Sync对象继承 AQS,  Syn ...

  2. 二叉树 & 平衡二叉树 算法(Java实现)

    二叉树 比如我要依次插入10.3.1.8.23.15.28.先插入10作为根节点: 然后插入3,比10小,放在左边: 再插入1,比10和3小,放在3左边: 再插入8,比10小,比3大,放在3右边: 再 ...

  3. Linux中常用命令cat

    cat可谓是随处可用,全名:concatenate and print files.用于连接和打印文件,按序读取文件内容,然后输出到标准输出.如果文件是单个破折号-或者缺乏文件,cat将读取标准输入. ...

  4. 开发dubbo应用程序(二)dubbo注册中心相关概述

    1.注册中心概述 ​ 在Dubbo微服务体系中,注册中心是其核心组件之一.Dubbo通过注册中心实现了分布式环境中各微服务之间的注册与发现,是各分布式节点之间的纽带.其主要作用如下: 动态加入.一个服 ...

  5. 解决WPF下popup不随着window一起移动的问题

    /// <summary> /// Popup帮助类,解决Popup设置StayOpen="True"时,移动窗体或者改变窗体大小时,Popup不随窗体移动的问题 // ...

  6. Java IO---序列化和反序列化

    一.序列化和反序列化介绍 什么是序列化和反序列化? ​ 序列化就是将对象转换为字节序列的过程. ​ 反序列化就是将字节序列恢复为对象的过程. 序列化的用途在哪? 通常情况下,序列化有两个用途: 将对象 ...

  7. tqdm()与set_description()的用法

    pbar=tqdm(range(55156))for i in pbar: # print(i) a=464443161*845113131 pbar.set_description("tr ...

  8. Python基础8

    jupyter notebook 安装目录插件, 步骤

  9. 【Java并发编程】24、Synchronized实现原理解析

    一.概述 我们知道在JDK1.5之前synchronized是一个重量级锁,相对于j.u.c.Lock,它会显得那么笨重,以至于我们认为它不是那么的高效而慢慢摒弃它. 不过,随着后续Java版本更新对 ...

  10. 【转载】C#中SqlCommand类的作用以及常用方法

    在C#的数据库操作过程中,SqlCommand类一般用于Sqlserver数据库的SQL语句的执行,包括Select语句.Update语句.Delete语句以及SQL存储过程等,SqlCommand的 ...