搭建 WPF 上的 UI 自动化测试框架
简要说明
OEA 1.0-2.0 框架中,界面都是以 WPF 技术作为基础平台开发的。我们需要对开发出来的系统进行自动化测试,而 .NET 平台的自动化测试平台在公司内部还没有其它部门完成,所以我们在 2010 年的时候使用 Ruby + VS UIUnitTest 开发了一个 UI 自动化(UI Automation,以下简称为UIA)框架,估且称其为 UIA 1.0。UIA 1.0 完全由周金根搭建,相关的内容,大家可以参考他写的这几篇文章:
《使用VS2010的CodedUI来做自己的自动化测试框架》
《信息系统开发平台OpenExpressApp:【OpenTest】 之 如何实现自动化测试框架》
该测试平台已经实现了由测试人员编写易读的 UI 自动化测试代码以完成日常的自动化测试需求,已经比较易用。部门用了大概1年左右,随着时间的推移,也显露了它的一些的不足:
- 调试困难,维护成本大,难以添加新的 UIA 支持。
- 过程式的代码,不结构化。
- 测试人员开发不易,不支持编译期检查错误,重复代码过多。
- 一些语句性能较低。 例如,许多类似于:"页签.页签.按钮"的代码,导致多次查找、进入页签,性能较低。
- 不支持客户化。
- 测试环境部署困难。 只有一台测试服务器搭建了该环境,开发人员不能使用 UIA 来辅助自己进行自测。
鉴于以上缺点,11年10月份我们决定使用 .NET 环境来搭建整个 UIA 框架,而不再使用 Ruby,同时尽量兼容测试人员编写的历史代码以及 API 风格。
使用演示
接下来,简单以我们目前的一个模块来说明一下 OEA 中的 UIA 是如何使用的。
测试人员会在 UIA 相应的工程中加入某个模块的 UIA 测试类,例如下图中的 PBS模板.cs 就是这个模块对应的测试:
其对应的代码如下:
class PBS模板 : GIX4测试用例
{
protected override void 运行()
{
打开当前测试模块("模板管理.PBS模板");
添加();
进入窗口("添加记录", 添加记录窗口 =>
{
属性编辑器("编码").输入("AutoTest01");
属性编辑器("名称").输入("自动化测试-PBS模板");
属性编辑器("备注").输入("测试模板");
点击按钮("确定");
保存();
});
//# 3 编辑PBS树>>>>>等待对树当前行的支持<<<<<
进入页签("PBS", PBS页签 =>
{
树型操作按钮通用测试();
保存();
进入页签("属性", 属性页签 =>
{
树型操作按钮通用测试();
保存();
进入页签("可选值", 可选值页签 =>
{
添加();
列表().当前行().属性编辑器("可选值").输入("AutoTest");
保存();
复制添加();
保存();
按住Ctrl();
列表().选择行(0);
释放Ctrl();
删除();
保存();
});
});
//#6 载入标准模板窗口中各页签
点击按钮("载入标准模板");
进入窗口("载入标准模板", 载入标准模板窗口 =>
{
页签("分部分项").单击();
页签("措施项目").单击();
页签("其它项目").单击();
页签("规费税金").单击();
点击按钮("确定");
});
});
//#6 修改PBS模板信息
点击按钮("修改");
进入窗口("修改", 修改窗口 =>
{
属性编辑器("编码").输入("AutoTest01-1");
属性编辑器("名称").输入("自动化测试-PBS模板-1");
属性编辑器("备注").输入("测试模板-1");
点击按钮("确定");
});
保存();
删除并确定();
}
}
UIA 测试环境就是一个简单的 WPF Applicatoin:
之前代码对应的软件运行时界面:
UIA 框架结构及相关重点
在整个 OEA 框架中,UIA 框架是一个重要的组成部分。目前只是实现了 WPF 客户端的 UIA,Web 的 UIA 将会在需要时添加:
OEA-UIA 的设计主要是基于 VS CodedUITest 中的核心类库,进行二次封装,提供更方便 OEA、更方便测试人员的的许多接口。引入如下类库:
其中的代码也不复杂,主要是在 WpfControl(继承自 UITestControl)的相关类型上添加一系列扩展方法,以下以一个按钮的点击为例:
public static WpfButton 按钮(this WpfControl context, string title = null)
{
return context.Find<WpfButton>(title);
}
public static WpfControl 单击(this WpfControl control)
{
control.EnsureClickable();
control.WaitForControlEnabled();
Mouse.Click(control);
return control;
}
public static TControl Find<TControl>(this WpfControl context, string title = null)
where TControl : WpfControl, new()
{
//if (TestContext.Current.NeedCancel) Playback.Cancel();
if (TestContext.Current.NeedCancel) throw new StopUIAException("停止自动化测试!");
var control = new TControl();
if (context != null)
{
control.Container = context;
}
if (!string.IsNullOrEmpty(title))
{
control.SearchProperties[WpfControl.PropertyNames.Name] = title;
}
return control;
}
这样,就可以在任何一个 WpfControl 容器上使用(例如页签 A) A.按钮(“添加”).单击() 了。
这里,需要特别说明的是,其实 UIAutomationClient 以及 UIAutomationTypes 并不是 VS CodedUITest 独有的程序集,而是 Windows 平台上的自动化框架程序集,相关的内容,可以看 MSDN 中的《MSDN - Accessibility》。基于这个框架,理论上可以做 windows 平台上所有的应用程序的自动化测试。
在我们 UIA 中,Windows Automation API 用于一些更加底层的控件查找场景。这是因为使用 VS CodedUITest 的类库,有时候并不能找到想要的控件,同时也不能为 OEA WPF 程序做一些深度的定制。OEA 中目前现在开发的 Web 框架,也同样会使用它来构建 UIA。
总结
其实这次重构并没有修改 UIA 1.0 版本的整个结构,主要是修改了语言环境为 .NET 环境,使得可以更加方便地添加各种功能,以及更好地和 OEA 框架整合。
BTW: 其实 OEA-UIA 已经在去年 11 月份就完成并应用,但是由于一直在开发 OEA 在 B/S 模式下的框架,所以迟迟没有发上来。不过最近 OEA-B/S 已经开发了个大概,借着写月度反思的机会,就把这篇文档写了写。欢迎交流。 :)
- 如何用Python提取中文关键词?
- 工信部:将加大网络提速降费力度加快百兆宽带普及
- 人工智能AI(5):线性代数之矩阵、线性空间
- iOS开发进阶篇——FRP与ReactiveCocoa的介绍(一)
- 英伟达修改GeForce软件使用条款:禁止在数据中心运行深度学习等应用
- 浅谈几种SLB技术的实现
- 史上最逼真人形机器人堪比健身教练,技能满满还会流汗
- 被监管前的疏忽?互联网金融大面积逾期,中介行为不容忽视
- 达尔文漏算的一步却让它填补,科学家认为人类最初认可的进化论不再适用
- 假如黑客攻击您的互联网汽车会怎么样?
- 享学课堂谈-Python初学者的设计模式入门
- 本体网络Ontology Zero入选国家工信部区块链开源项目计划
- 完全无人驾驶将首先在中国大城出现
- 榴莲售出的三拼域名ancaiyun.com 已建站
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- TypeScript 类型系统
- 图解 Java 线程生命周期
- sql 基础命令
- python自学成才之路 列表,元组,集合详细用法
- 备战秋招-面经篇-[二十一]
- 高性能 Java 应用层网关设计实践
- redis实战第十五篇 redis cluster的批处理中ask重定向解决方案
- 干货 | Elasticsearch 运维实战常用命令清单
- 备战秋招-面经篇-[二十二]
- 快速上手Spring-Data-Redis
- Lua 5.1 参考手册
- 图文详解k8s自动化持续集成之GitLab CI/CD
- Harbor v2.0 镜像回收那些事
- redis实战第十四篇 redis cluster ask重定向
- Go命令官方指南【原译】