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

news2024/11/23 12:58:08

文章目录

  • 前言
  • 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编程。

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

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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

使用这 7 个绩效评估模板简化您的员工评估

绩效评估受到了不好的评价;员工发现它们压力很大,而管理者则发现它们很耗时。 但随着绩效管理成为 2024 年人力资源的首要任务,也许是时候重新思考了。绩效评估模板可以帮助减轻评估过程的麻烦。通过为管理者提供一种简单、标准化的方法来评…

CVE-2024-3116 PgAdmin8.4代码执行漏洞

前言 在有闲情的时候,看了一下最近的CVE,看到了pgAdmin4在8.4版本之前存在着一个远程代码执行漏洞,因为pgAdmin4在github是开源的,网上也没有看到分析文章,于是就把源码下载了下来,根据漏洞的描述大致的分…

Linux服务器终端软件termius以及Xshell + WinSCP组合

1. termius 官网地址:https://termius.com/ Termius是一个跨平台的SSH客户端,它提供了一个便捷的方式来远程连接和管理服务器、虚拟机和网络设备。以下是Termius的一些特点和功能: 跨平台支持:Termius可在多个操作系统上运行&…

JS事件循环、宏任务与微任务

在JavaScript中,事件循环(Event Loop)是处理异步操作的核心机制。它负责执行代码,处理事件,并在适当的时候调度回调。为了更好地理解JavaScript的执行模型,我们需要深入探讨事件循环、宏任务(Ma…

分布式与一致性协议之Raft算法(二)

Raft算法 什么是任期 我们知道,议会选举中的领导者是有任期的,当领导者任命到期后,需要重新再次选举。Raft算法中的领导者也是有任期,每个任期由单调递增的数字(任期编号)标识。比如,节点A的任期编号是1。任期编号会…

基于Docker + Locust的数据持久化性能测试系统

前几天给大家分享了如何使用Locust进行性能测试,但是在实际使用中会发现存在压测的结果无法保存的问题,比如在分布式部署情况下进行压测,每轮压测完成需要释放资源删除容器重新部署后,这段时间的压测结果就都丢失了,如…

Nacos 安全零信任实践

作者:柳遵飞 Nacos 作为配置中心经常存储一些敏感信息,但是由于误用导致安全风险,最常见的主要是以下两个问题: 1)Nacos 暴露公网可以吗?不可以,因为 Nacos 定位是注册配置中心,是…

【STM32+HAL】SDIO模式读写SD卡

一、准备工作 有关CUBEMX的初始化配置,参见我的另一篇blog:【STM32HAL】CUBEMX初始化配置 二、所用工具 1、芯片: STM32F407ZGT6 2、IDE: MDK-Keil软件 3、库文件:STM32F4xxHAL库 三、实现功能 实现用DMA读写SD卡内…

Hadoop3:集群搭建及常用命令与shell脚本整理(入门篇,从零开始搭建)

一、集群环境说明 1、用VMware安装3台Centos7.9虚拟机 2、虚拟机配置:2C,2G内存,50G存储 3、集群架构 从表格中,可以看出,Hadoop集群,主要有2部分,一个是HDFS服务,一个是YARN服务 …

CSS中的层叠上下文

HTML 文档中的三维概念 平时我们从设备终端看到的 HTML 文档都是一个平面的,事实上 HTML 文档中的元素却是存在于三个维度中。除了大家熟悉的平面画布中的 x 轴和 y 轴,还有控制第三维度的 z 轴。 其中 x 轴通常用来表示水平位置,y 轴来表示…

力扣刷题Day2

题目链接: 24. 两两交换链表中的节点 - 力扣(LeetCode) 效果: 解题思路: 给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。 注意不可以只是单纯的改变节点内部的值,而…

面试:MYSQL(SQL优化、MYSQL事务)

目录 一、SQL优化 1、如何定位慢查询 (1)方案一:开源工具 (2)方案二:慢日志查询 2、定位到慢查询时,如何优化 3、什么是索引 (1)底层结构 4、聚簇索引&#xff0…

linux学习:线程安全(信号量+互斥锁读写锁+条件变量+可重入函数)

目录 信号量 有名信号量 步骤 api 创建、打开一个POSIX有名信号量 对 POSIX 有名信号量进行 P、V 操作 关闭、删除 POSIX 有名信号量 例子 无名信号量 步骤 api 初始化、销毁 POSIX 无名信号量 互斥锁读写锁 例子 两条线程 使用互斥锁来互斥地访问标准输出 在加锁…

UE Snap03 启动参数设置

UE Snap03 启动参数设置 UE打包后传入自定义参数及解析。 void UGameInstance::StartGameInstance() {Super::StartGameInstance();UE_LOG(LogTemp, Warning, TEXT("--StartGameInstance--"));FString param;FParse::Value(FCommandLine::Get(), TEXT("-UserN…

# 谷歌 Chrome 浏览器无法安装插件的解决方法

谷歌 Chrome 浏览器无法安装插件的解决方法 运用开发模式安装 安装步骤: 1、 将 XX.crx 插件的扩展名改成 .zip 或者 .rar 并解压到文件夹 XX 目录。 1)如:下载的 前端框架 vue.js 插件 nhdogjmejiglipccpnnnanhbledajbpd-6.6.1-Crx4Chro…

Isaac Sim 2 (学习笔记4.26)

今天一整天都要开会,闲的无聊,把这周学的东西简单整理下。纯英文文档想不起来东西的时候总是找不到位置...持续更新一整天 1.将块与块连接起来 尝试连接块与块的时候发现只能是cube、mesh连接,如果是一整个的包括坐标系、材质包等等&#xf…

阿里云服务器购买和设置

🐌博主主页:🐌​倔强的大蜗牛🐌​ 📚专栏分类:服务器❤️感谢大家点赞👍收藏⭐评论✍️ 目录 1、搜索阿里云网址: 2、点击产品,选择云服务器ECS 3、选择立即购买 4、选…

C# Web控件与数据感应之 Control 类

目录 关于数据感应 Control 类 范例运行环境 simpleDataListEx方法 设计 实现 调用示例 数据源 调用 小结 关于数据感应 数据感应也即数据捆绑,是一种动态的,Web控件与数据源之间的交互,诸如 ListControl 类类型控件,在…

uni-app - 使用地图功能打包安卓apk的完美流程以及重要的注意事项(带您一次打包成功)

在移动应用开发中,地图功能是一个非常常见且实用的功能,可以帮助用户快速定位并浏览周边信息。而在uni-app开发中,使用地图功能也是一项必备技能。本文将介绍uni-app使用地图功能打包安卓apk的注意事项,帮助开发者顺利完成地图功能…

万兆以太网MAC设计(12)万兆UDP协议栈上板与主机网卡通信

文章目录 一、设置IP以及MAC二、上板效果2.1、板卡与主机数据回环测试2.2、板卡满带宽发送数据 一、设置IP以及MAC 顶层模块设置源MAC地址 module XC7Z100_Top#(parameter P_SRC_MAC 48h01_02_03_04_05_06,parameter P_DST_MAC 48hff_ff_ff_ff_ff_ff )(input …