PC 自动化测试入门 - pywinauto 上篇:初识

文章目录

  • 前言
  • PC 自动化测试 是什么?
  • 常用 PC 自动化测试工具
  • pywinauto 是什么?
    • Windows上支持的可访问性技术列表
  • 操作记事本自动写入
    • 问题
      • `app = Application(backend="uia").start("notepad.exe")` 无法正常启动
      • 组件选择器和 `print_control_identifiers` 打印的组件不匹配
      • 多个相同组件定位问题
  • 参考
  • 个人简介

前言

  • 在软件开发领域,自动化测试是一项重要的实践,它可以提高测试效率、减少人力成本,并确保软件质量。PC 自动化测试特指针对 Windows 平台的应用程序进行自动化测试,而 pywinauto 是一款用于实现 Windows GUI 应用程序自动化测试的 Python 库。本文将介绍 PC 自动化测试的基本概念、常用工具以及初识 pywinauto,以及通过一个操作记事本的示例演示其基本用法。

PC 自动化测试 是什么?

  • PC 自动化测试是指利用自动化工具或脚本来模拟用户操作,对 Windows 平台的应用程序进行功能测试、性能测试等,以验证其是否符合预期行为。相比手动测试,自动化测试可以提高测试效率、减少测试成本,并且可以在持续集成和持续交付流程中实现自动化测试。

常用 PC 自动化测试工具

  • 在 PC 自动化测试领域,有许多常用的工具,如:
- Selenium:用于 Web 应用程序的自动化测试。
- pywinauto:用于 Windows GUI 应用程序的自动化测试。
- AutoIt:用于 Windows 平台的自动化测试,支持模拟键盘和鼠标操作。

pywinauto 是什么?

  • pywinauto 是一款基于 Python 的开源库,用于自动化测试 Windows 平台的 GUI 应用程序。它能够模拟用户的键盘和鼠标操作,以及获取和修改应用程序的控件属性。pywinauto 提供了简单而强大的 API,使得开发人员可以轻松地编写自动化测试脚本。

Windows上支持的可访问性技术列表

  • 一旦你安装了pywinauto,第一件必要的事情是确定您的应用程序可以使用哪种可访问性技术(pywinauto的后端)。常用的有 Win32 API、MS UI。

  • 如果你不知道程序到底适用于那种可访问技术,可以借助于GUI对象检查工具来做,常用的检查工具有Inspect.exe,Spy++ 等,下面以 Inspect.exe 为例:

操作记事本自动写入

  • 环境:win 10、Python 3.12
from pywinauto import Application

# 连接 PC 应用的两种方式
# app = Application(backend="uia").start("notepad.exe")
app = Application(backend="uia").connect(process=32120)

print(app.process)

# 获取主窗口
top_window = app.window(title="无标题 - Notepad", control_type="Window")
# 打印控件菜单树结构
top_window.print_control_identifiers()

# 获取输入框
document = top_window.child_window(control_type="Document")

document.print_control_identifiers()
# 标出是否正确选中输入框
document.draw_outline(colour='red')

# 写入
document.click_input()
document.type_keys(keys="Your text here", with_spaces=True)
  • 输出结果:
32120
Control Identifiers:

Dialog - '无标题 - Notepad'    (L-1512, T194, R-192, B886)
['Dialog', '无标题 - NotepadDialog', '无标题 - Notepad']
child_window(title="无标题 - Notepad", control_type="Window")
   | 
   | Pane - ''    (L-1505, T280, R-199, B879)
   | ['Pane', '无标题Pane', 'Pane0', 'Pane1', '无标题Pane0', '无标题Pane1']
   |    | 
   |    | Document - ''    (L-1505, T280, R-199, B879)
   |    | ['Document', '无标题Document']
   | 
   | Pane - ''    (L-1459, T205, R-1173, B237)
   | ['Pane2', '无标题Pane2']
   |    | 
   |    | TabControl - ''    (L-18704, T-17045, R-1129, B241)
   |    | ['TabControl', 'TabControl添加新标签页']
   |    | child_window(auto_id="Tabs", control_type="Tab")
   |    |    | 
   |    |    | ListBox - ''    (L-18704, T-17045, R-1169, B241)
   |    |    | ['ListBox']
   |    |    | child_window(auto_id="TabListView", control_type="List")
   |    |    |    | 
   |    |    |    | TabItem - '无标题. 未修改。'    (L-1455, T205, R-1173, B241)
   |    |    |    | ['无标题. 未修改。TabItem', '无标题. 未修改。', 'TabItem']
   |    |    |    | child_window(title="无标题. 未修改。", control_type="TabItem")
   |    |    |    |    | 
   |    |    |    |    | Static - '无标题'    (L-1440, T214, R-1398, B232)
   |    |    |    |    | ['Static', '无标题', '无标题Static']
   |    |    |    |    | child_window(title="无标题", control_type="Text")
   |    |    |    |    | 
   |    |    |    |    | Button - '关闭标签页'    (L-1219, T209, R-1183, B236)
   |    |    |    |    | ['Button', '关闭标签页', '关闭标签页Button', 'Button0', 'Button1']
   |    |    |    |    | child_window(title="关闭标签页", auto_id="CloseButton", control_type="Button")
   |    |    | 
   |    |    | Button - '添加新标签页'    (L-1166, T210, R-1130, B237)
   |    |    | ['Button2', '添加新标签页Button', '添加新标签页']
   |    |    | child_window(title="添加新标签页", auto_id="AddButton", control_type="Button")
   |    | 
   |    | Pane - '记事本自动保存进度。下次打开记事本时,你的所有内容都将可用。'    (L0, T0, R0, B0)
   |    | ['记事本自动保存进度。下次打开记事本时,你的所有内容都将可用。Pane', '记事本自动保存进度。下次打开记事本时,你的所有内容都将可用。', 'Pane3']
   |    | child_window(title="记事本自动保存进度。下次打开记事本时,你的所有内容都将可用。", auto_id="TeachingTip", control_type="Pane")
   | 
   | Pane - ''    (L-1505, T242, R-365, B275)
   | ['Pane4', '无标题Pane3']
   |    | 
   |    | Menu - ''    (L-1505, T242, R-1310, B278)
   |    | ['Menu', '无标题Menu', 'Menu0', 'Menu1']
   |    | child_window(auto_id="MenuBar", control_type="MenuBar")
   |    |    | 
   |    |    | MenuItem - '文件'    (L-1500, T242, R-1444, B278)
   |    |    | ['文件', '文件MenuItem', 'MenuItem', 'MenuItem0', 'MenuItem1']
   |    |    | child_window(title="文件", auto_id="File", control_type="MenuItem")
   |    |    | 
   |    |    | MenuItem - '编辑'    (L-1435, T242, R-1380, B278)
   |    |    | ['编辑', '编辑MenuItem', 'MenuItem2']
   |    |    | child_window(title="编辑", auto_id="Edit", control_type="MenuItem")
   |    |    | 
   |    |    | MenuItem - '查看'    (L-1370, T242, R-1314, B278)
   |    |    | ['查看', '查看MenuItem', 'MenuItem3']
   |    |    | child_window(title="查看", auto_id="View", control_type="MenuItem")
   |    | 
   |    | Button - '设置'    (L-237, T243, R-203, B277)
   |    | ['Button3', '设置', '设置Button']
   |    | child_window(title="设置", auto_id="SettingsButton", control_type="Button")
   | 
   | TitleBar - ''    (L0, T0, R0, B0)
   | ['TitleBar']
   |    | 
   |    | Menu - '系统'    (L-1503, T203, R-1478, B228)
   |    | ['Menu2', '系统', '系统Menu', '系统0', '系统1']
   |    | child_window(title="系统", auto_id="MenuBar", control_type="MenuBar")
   |    |    | 
   |    |    | MenuItem - '系统'    (L-1503, T203, R-1478, B228)
   |    |    | ['MenuItem4', '系统MenuItem', '系统2']
   |    |    | child_window(title="系统", control_type="MenuItem")
   |    | 
   |    | Button - '最小化'    (L-356, T195, R-303, B229)
   |    | ['Button4', '最小化Button', '最小化']
   |    | child_window(title="最小化", control_type="Button")
   |    | 
   |    | Button - '最大化'    (L-303, T195, R-251, B229)
   |    | ['Button5', '最大化', '最大化Button']
   |    | child_window(title="最大化", control_type="Button")
   |    | 
   |    | Button - '关闭'    (L-251, T195, R-198, B229)
   |    | ['Button6', '关闭', '关闭Button']
   |    | child_window(title="关闭", control_type="Button")
Control Identifiers:

Document - ''    (L-1505, T280, R-199, B879)
['Document']

问题

app = Application(backend="uia").start("notepad.exe") 无法正常启动

app = Application(backend="uia").start("notepad.exe")
print(app.process)

打印的进程ID为 2643,但实际进程ID为 836,导致无法查找到元素:
pywinauto.findwindows.ElementNotFoundError: {'title': '无标题 - Notepad', 'control_type': 'Window', 'backend': 'uia', 'process': 21300}

有知道朋友可以帮忙解答一下,十分感谢

组件选择器和 print_control_identifiers 打印的组件不匹配

  • 使用了几个组件选择器:Inspect.exe、Spy++,感觉 Inspect.exe 最好用,但组件选择器和组件树不匹配,需要以打印的组件树为准。
  • 比如上面 demo 中关于输入框组件:
  • 组件树的打印结果:
   |    | Document - ''    (L-1505, T280, R-199, B879)
   |    | ['Document', '无标题Document']
  • Inspect.exe 的结果:

  • Inspect.exe 的结果中有 name 字段,但实际上组件并没有 name,使用 name 会导致组件无法匹配到。

多个相同组件定位问题

  • 当使用 child_window 方法查找组件元素时,我们使用单一条件可能查到到多个组件,我们可以使用多个条件来尽可能确定唯一元素,比如下面这些条件:

参考

  • 官方文档

个人简介

👋 你好,我是 Lorin 洛林,一位 Java 后端技术开发者!座右铭:Technology has the power to make the world a better place.

🚀 我对技术的热情是我不断学习和分享的动力。我的博客是一个关于Java生态系统、后端开发和最新技术趋势的地方。

🧠 作为一个 Java 后端技术爱好者,我不仅热衷于探索语言的新特性和技术的深度,还热衷于分享我的见解和最佳实践。我相信知识的分享和社区合作可以帮助我们共同成长。

💡 在我的博客上,你将找到关于Java核心概念、JVM 底层技术、常用框架如Spring和Mybatis 、MySQL等数据库管理、RabbitMQ、Rocketmq等消息中间件、性能优化等内容的深入文章。我也将分享一些编程技巧和解决问题的方法,以帮助你更好地掌握Java编程。

🌐 我鼓励互动和建立社区,因此请留下你的问题、建议或主题请求,让我知道你感兴趣的内容。此外,我将分享最新的互联网和技术资讯,以确保你与技术世界的最新发展保持联系。我期待与你一起在技术之路上前进,一起探讨技术世界的无限可能性。

📖 保持关注我的博客,让我们共同追求技术卓越。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/583468.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

clickhouse学习笔记05

ClickHouseSpringBoot2.XMybatisPlus整合搭建 添加需要的依赖: 添加clickhouse依赖: 配置数据库配置: 我们框架就搭建完了。 ClickHouse的项目案例统计需求讲解 ClickHouse的项目案例统计库表和数据准备 添加数据: 数据都插入进来…

算法必备数学基础:图论方法由浅入深实践与应用

作者介绍:10年大厂数据\经营分析经验,现任大厂数据部门负责人。 会一些的技术:数据分析、算法、SQL、大数据相关、python 欢迎加入社区:码上找工作 作者专栏每日更新: LeetCode解锁1000题: 打怪升级之旅 python数据分析…

python 入门第三天(高级进阶:str、set、dict、slice、推导式、高级变量类型的公共语法)

一、字符串str 字符串就是一串字符,是编程语言中表示文本的数据类型 1. 字符串定义 Python中可以使用一对双引号或者一对单引号定义字符串 str1 hello str2 "hello" 2. 获取字符串中元素 和列表一样,字符串也是通过索引获取元素 str …

CentOS7上安装部署Consul服务(小白版)

文章目录 1.Consul服务介绍2.Consul服务下载安装3.Consul服务配置3.1.创建Consul服务的运行用户3.2.下载服务配置生成脚本3.3.配置执行脚本需要的临时变量3.4.生成配置文件3.5.启动测试3.6.开机自启配置 1.Consul服务介绍 Consul 是一种开源的服务网格解决方案,由 …

pytorch库 01 安装Anaconda、Jupyter,Anaconda虚拟环境连接pycharm

文章目录 一、安装Anaconda1、卸载Anaconda(可选)2、下载并安装Anaconda3、配置环境变量4、桌面快捷方式 二、安装 PyTorch(GPU 版)库1、创建虚拟环境,并安装一些常用包2、GPU 基础3、检查驱动4、安装CUDA(…

Linux搭建局域网私有yum仓库/配置本地光盘镜像仓库/搭建公有yum仓库--7700字详谈

帮助与补全功能 1.补全 yum (options)COMMAND check check-update clean deplist downgrade erase fs fssnapshot groups help history info install list makecache provides reinstall repo-pkgs repolist search shell swap update update-minimal …

每周一算法:单源次短路

题目描述 “您的个人假期”旅行社组织了一次比荷卢经济联盟的巴士之旅。 比荷卢经济联盟有很多公交线路。每天公共汽车都会从一座城市开往另一座城市。沿途汽车可能会在一些城市(零或更多)停靠。 旅行社计划旅途从 S S S 城市出发,到 F …

新书速览|ChatGLM3大模型本地化部署、应用开发与微调

实战文本生成、智能问答、信息抽取、财务预警应用开发,掌握ChatGLM3大模型部署、开发与微调技术 01 本书内容 《ChatGLM3大模型本地化部署、应用开发与微调》作为《PyTorch 2.0深度学习从零开始学》的姊妹篇,专注于大模型的本地化部署、应用开发以及微…

挤压激励注意力 SE | Squeeze-and-Excitation Networks

论文名称:《Squeeze-and-Excitation Networks》 论文地址:https://arxiv.org/pdf/1709.01507.pdf 代码地址: https://github.com/hujie-frank/SENet 卷积神经网络 (CNN) 的核心构建块是卷积运算符,它使网络能够通过在每一层的局…

C++ | Leetcode C++题解之第50题Pow(x,n)

题目: 题解: class Solution { public:double quickMul(double x, long long N) {if (N 0) {return 1.0;}double y quickMul(x, N / 2);return N % 2 0 ? y * y : y * y * x;}double myPow(double x, int n) {long long N n;return N > 0 ? qu…

谷歌CEO谈拥有“最好的”AI、1000 种新云产品和Workspace

谷歌首席执行官桑达尔皮查伊 (Sundar Pichai) 在谷歌财报中发表了大胆言论,其中包括将 Workspace 吹捧为网络安全领域的领导者、谷歌云和 YouTube 到今年年底的总运行额将达到 1000 亿美元,以及为什么需要“强大的合作伙伴计划”来推动人工智能发展。 谷…

70、栈-最小栈

思路: 除了最后一个获取最小值以外,其他都可以使用一个栈来实现,但是如果当前一个最小值被移除了,如果获取第二小的值,这个是需要记录的。所以最好的办法是两个栈。一个作为主栈存放数据,一个作为辅栈&…

C++之类和对象

目录 一:再谈构造函数 1.1 构造函数体赋值 1.2 初始化列表 1.3 explicit关键字 二. static成员 2.2 特性 三. 友元 3.1 友元函数 3.2 友元类 四: 内部类 五:匿名对象 六. 再次理解类和对象 一:再谈构造函数 1.1 构造…

关于discuz论坛网址优化的一些记录(网站地图sitemap提交)

最近网站刚上线,针对SEO做了些操作,为了方便网站网页百度被收录,特此记录下 discuz有免费的sitemap插件可以用,打开后台管理,找到插件栏,然后找到更多插件,进入插件市场。 选择这个免费的sitem…

ios CI/CD 持续集成 组件化专题四-(手动发布私有库-组件化搭建)

一 、创建私有索引库 1.1 、第一步 首先检查本地是否存在需要的私有索引库 pod repo list 例如:dp_base_ios_spec 在本地不存在该私有索引库 1.2 、第二步 在git下下创建一个新的库,这个库用来保存私有库的podspec文件,取名叫xxxSpec用以…

计算机组成实验(5)

一、实验目的和要求 1.1 实验目的 1. 复习二进制加减、乘除的基本法则 2. 掌握补码的基本原理和作用 3. 了解浮点数的表示方法及加法运算法则 4. 进一步了解计算机系统的复杂运算操作 1.2 实验要求 1. 熟悉二进制原码补码的概念,了解二进制加减乘除的原理与操作实现。 …

力扣HOT100 - 207. 课程表

解题思路&#xff1a; class Solution {public boolean canFinish(int numCourses, int[][] prerequisites) {int[] inDegree new int[numCourses];//存每个结点的入度List<List<Integer>> res new ArrayList<>();//存结点之间依赖关系Queue<Integer>…

buuctf——web题目练习

1.极客大挑战2019 easysql 密码或者用户输入万能密码即可 关于万能密码的理解和原理&#xff0c;可以参考这篇BUUCTF[极客大挑战 2019] EasySQL 1_[极客大挑战 2019]easysql 1-CSDN博客 2.极客大挑战2019 have fun 题目源码 需要构造payload 网页传参可参考&#xff1a;…

设计模式 基本认识

文章目录 设计模式的作用设计模式三原则设计模式与类图设计模式的分类 设计模式的作用 设计模式是在软件设计过程中针对常见问题的解决方案的一种通用、可重用的解决方案。设计模式提供了一种经过验证的方法&#xff0c;可以帮助开发人员解决特定类型的问题&#xff0c;并在软…

C++常用的输入输出方法(ACM模式)

文章目录 前言一、输入输出方法1、cin2、getline()3、getchar() 二、算法案例1、一维数组1.1 输入固定长度1.2长度不固定 2、固定二维数组3、以非空格隔开的元素输入3、常见数据结构定义以及输入3.1 链表 前言 C中的输入输出函数有很多&#xff0c;我们本章只针对大部分算法题…