1.简介
Windows系统安装的软件如何自动化测试呢?Python的一个模块说是它可以实现Microsoft Windows GUI自动化测试,目前流行的自动化就是web,app,接口算是目前最主流的测试内容了,那么如果让做安装windows上的客户端自动化呢?是不是一脸懵?今天rebort就给大家讲解和分享一下啊!
2.pywinauto
pywinauto 是一个用于 **Windows **GUI **自动化的 **Python 库。它允许你编写脚本来自动化 Windows 应用程序的操作,如点击按钮、输入文本、选择菜单项等。pywinauto 基于 pywin32 和 comtypes,提供了更高级和更易用的接口。
官网地址:https://pywinauto.readthedocs.io/en/latest/index.html
中文文档地址:https://www.kancloud.cn/gnefnuy/pywinauto_doc/1193035
3.环境准备
3.1操作系统
1.rebort的环境是Windows 11版本 64位系统(32位的同学自己想办法哦,当然了现在32位的一般很少见),如下图所示:
Python版本:我的是这个,python:3.11.1,如下图所示:
4.环境搭建
Pywinauto是一个强大的Python库,用于自动化Windows应用程序的界面操作。无论是进行GUI测试、数据采集还是自动化任务,Pywinauto都能提供便捷的解决方案。
4.1安装Python
首先我们要安装好Python,然后才能安装Pywinauto,原因就不用我多说了吧!因为Pywinauto是Python的库,所有得现有父亲才有儿子。具体如何安装Python,在这里就不再赘述了,不会的小伙伴可以看rebort之前的博客文章 。
4.2安装Pywinauto
4.2.1方法一(推荐)
1.和安装其他依赖库一样,直接使用命令安装即可。可能要稍等一会....只需要一条命令,命令如下:
pip install pywinauto
4.2.2方法二(推荐)
1.确保你已经成功安装Python和Pycharm编辑器,点击"File" -> "Settings" -> "Project" -> "Python Interpreter"在"Project lnterpreter"下,点击"Add"并在搜索栏中输入要导入的模块名称搜索pywinauto,然后点击“install”即可
4.2.3方法三(不推荐)
在网站:https://github.com/pywinauto/pywinauto/releases 中下载最新的pywinauto,解压后,进入对应的目录中,cmd,然后运行命令如下:
python setup.py install
4.3验证是否安装成功Pywinauto
4.3.1导入对应库
1.Docs命令窗口
直接打开docs命令窗口输入python,进行导入对应的库,看是否报错,如下图所示:
2.Pycharm编辑器
打开Pycharm编辑器,在Python脚本中导入Pywinauto的必要模块,通常包括Application
和findwindows,看是否报错,如下图所示:
安装前(红色报错),如下图所示:
安装后(红色报错消失),如下图所示:
4.3.2通过pip查看
通过pip list命令查看,如下图所示:
到此,环境就搭建好了
5.使用说明
安装好了之后,关于使用说明大家可以参照官方文档上的说明。
5.1可访问性技术(backend)
就是说在我们安装好Pywinauto之后,在启动应用程序之前,首先要确定哪种可访问性技术(pywinauto的backend)可以用于我们的应用程序,在windows上受支持的辅助功能技术有两种:
-
Win32 API (backend= “win32”) ,默认的backend, MFC、VB6、VCL、简单的 WinForms 控件和大多数旧的遗留应用程序
-
MS UI Automation (backend=”uia”) , WinForms、WPF、商店应用程序、Qt5、浏览器
那么如何知道程序到底适用于那种backend? 可以借助于GUI对象检查工具来确定程序到底适用于那种backend。如果使用 inspect 的uia模式,可见的控件和属性更多的话,backend可选uia,反之,backend可选win32。
5.2inspect 元素定位工具
常用的一些元素定位工具: SPY++,用于WIN32 API。当SPY++可以显示所有的控件时,访问技术应该选择”win32”。 Inspect.exe:如果Inspect.exe的模式设置为UIA模式,可以比SPY++显示更多的控件,则访问技术应该选择”uia”。 py_inspect:支持win32和uia两种访问技术,是SWAPY的替代。 UISPY:支持uia访问技术。 SWAPY:只支持win32访问技术。
1.inspect 教程:https://learn.microsoft.com/zh-cn/windows/win32/winauto/inspect-objects,如下图所示:
2.inspect.exe 是windows SDK 的一个内置工具,可以通过先安装Windows SDK,下载地址:https://developer.microsoft.com/en-us/windows/downloads/windows-sdk/ 如下图所示:
3.下载后在本机安装,Inspect.exe 位于 SDK 安装路径的 \bin\文件夹中。通常不需要以管理员身份运行它。如下图所示:
4.它可以单独复制出来,放在桌面使用。
5.2.1“检查”窗口
“检查”窗口有多个主要部分:
-
标题栏。 显示“检查”窗口句柄 (HWND)。
-
菜单栏。 提供对“检查”功能的访问。
-
工具栏。 提供对“检查”功能的访问。
-
树状视图。 将 UI 元素的分层结构呈现为树视图控件,可用于在元素之间导航。
-
数据视图。 显示所选 UI 元素的所有公开辅助功能属性。
1.菜单栏中提供的命令也可以在工具栏中使用。 下图显示了“检查”查询记事本中的“编辑”菜单元素的 UI 自动化属性。如下图所示:
2.以下是操作微信时候,工具的一些展示和变化,如下图所示:
5.3一些类似的工具
5.3.1Python tools
-
PyAutoGui - 一个流行的跨平台库(具有基于图像的搜索,没有基于文本的控件操作)。
-
Lackey - Sikuli的纯Python替代品(基于图像模式匹配)。
-
AXUI - MS UI Automation API的一个包装器。
-
winGuiAuto - 另一个使用Win32 API的模块。
5.3.2其他脚本语言工具
-
(Perl) Win32::GuiTest
-
(Ruby) Win32-Autogui - Win32 API的包装器。
-
(Ruby) RAutomation - 有3个适配器:Win32 API,UIA,AutoIt。
5.3.3其他免费工具
-
(C#) Winium.Desktop - 一个年轻但很好的基于MS UI Automation的工具。
-
(C#) TestStack.White - 另一个很好的基于MS UI Automation的库,历史悠久。
-
AutoIt - 具有自己的类似Basic语言的免费工具(基于Win32 API,没有.NET计划)
-
AutoHotKey - 具有自己的脚本语言的原生C ++工具(.ahk)
5.3.4商业工具
-
WinRunner (http://www.mercury.com/us/products/quality-center/functional-testing/winrunner/)
-
SilkTest (http://www.segue.com/products/functional-regressional-testing/silktest.asp)
-
Many Others (http://www.testingfaqs.org/t-gui.html)
5.4gui inspect tool全家桶
1.官方文档中推荐使用spy++和inspect来检查。可自行下载:
2、下载解压后的各种工具,如下展示:
Spy++ (定位元素工具(win32))
Inspect(定位元素工具(uia))
UI Spy (定位元素工具)
Swapy(可简单生成pywinauto代码)
5.5如何判断程序的backend是’win32’还是’uia’呢?
目前大部分应用程序主窗口是“uia”,一般都是先用“uia',报错时再换成'win32'。这也是一种笨办法,二者选一,不是这个就是那个。
这里按官方推荐的介绍使用inspect来判断backend的类别,具体步骤如下:
1.下载上面github链接里的相关工具并打开,点击inspect左上角的下拉列表中切换到UI Automation,如下图所示:
2.然后鼠标点一下你需要测试的程序窗体,inspect就会显示相关信息。 上图为点击window文件夹下的微信的结果,inspect中显示了相关的信息,如下图所示。说明backend为uia。如上图所示:
3.相反,没有切换,点击任务管理器,拒绝访问,则为win32,如下图所示:
6.快速入门
这里以启动Windows自带的计算器为例,给各位小伙伴或者童鞋们来演示一下。
6.1代码设计
# 3.导入模块 from pywinauto import Application from pywinauto import findwindows #启动计算器 app = Application().start("calc.exe")
2.运行代码后电脑端的动作(启动计算器)
7.梳理
介绍了Pywinauto的基本使用方法,包括安装、导入模块、启动应用程序、如何判断程序的backend是’win32’还是’uia’。通过掌握这些基本概念和技巧,可以利用Pywinauto轻松实现自动化Windows界面操作,提高工作效率。
接下来我们看看使用其他方法打开计算器和非电脑自带的程序,pywinauto 可以启动电脑自带的应用程序,也可以启动电脑安装的应用程序。
8.运行指定的应用程序
运行指定的应用程序,顾名思义就是用户指定运行哪款程序,就运行哪款程序。这里将其分为两大类:电脑自带的应用程序,如:记事本、计算器等和电脑安装的应用程序,如:QQ、微信等。分好后,接下来看看是如何这两类应用程序的。
8.1启动电脑自带的应用程序
上面已经介绍启动计算器了,下面启动一下记事本,演示一下。
通过start() 方法指定exe应用程序的名称即可。start()方法用于启动一个可执行程序
def start(self, cmd_line, timeout=None, retry_interval=None, create_new_console=False, wait_for_idle=True, work_dir=None): """Start the application as specified by cmd_line""" # try to parse executable name and check it has correct bitness if'.exe'in cmd_line and self.backend.name == 'win32': exe_name = cmd_line.split('.exe')[0] + '.exe' _warn_incorrect_binary_bitness(exe_name) if timeout is None: timeout = Timings.app_start_timeout if retry_interval is None: retry_interval = Timings.app_start_retry start_info = win32process.STARTUPINFO() # we need to wrap the command line as it can be modified # by the function command_line = cmd_line # Actually create the process dw_creation_flags = 0 if create_new_console: dw_creation_flags = win32con.CREATE_NEW_CONSOLE try: (h_process, _, dw_process_id, _) = win32process.CreateProcess( None, # module name command_line, # command line None, # Process handle not inheritable. None, # Thread handle not inheritable. 0, # Set handle inheritance to FALSE. dw_creation_flags, # Creation flags. None, # Use parent's environment block. work_dir, # If None - use parent's starting directory. start_info) # STARTUPINFO structure. except Exception as exc: # if it failed for some reason message = ('Could not create the process "%s"\n' 'Error returned by CreateProcess: %s') % (cmd_line, str(exc)) raise AppStartError(message) self.process = dw_process_id if self.backend.name == 'win32': self.__warn_incorrect_bitness() def app_idle(): """Return true when the application is ready to start""" result = win32event.WaitForInputIdle( h_process, int(timeout * 1000)) # wait completed successfully if result == 0: return True # the wait returned because it timed out if result == win32con.WAIT_TIMEOUT: return False return bool(self.windows()) # Wait until the application is ready after starting it if wait_for_idle and not app_idle(): warnings.warn('Application is not loaded correctly (WaitForInputIdle failed)', RuntimeWarning) self.actions.log("Started " + cmd_line + " application.") return self
相关参数:
-
cmd_line: 是包含路径的启动应用程序的命令以及启动参数
-
timeout: 启动程序的超时时钟设置,默认为5s
-
create_new_console: 创建新的控制台,默认不创建
-
wait_for_idle: 是否等待到程序的Idle状态
-
work_dir: 指定工作目录
8.1.1代码设计
# 3.导入模块 from pywinauto.application import Application # 启动记事本 app = Application(backend="uia").start("notepad.exe")
8.1.2运行代码
1.运行代码,右键Run'Test'
2.运行代码后电脑端的动作(启动记事本)。
8.2启动电脑安装的应用程序
这里以大家熟悉的微信为例,属性-查看快捷方式,如下图所示:
8.2.1代码设计
# 3.导入模块 from pywinauto.application import Application # 启动微信 app = Application(backend="uia").start(r"D:\WeChat\WeChat.exe")
8.1.3运行代码
1.运行代码,右键Run'Test',就可以看到控制台输出:
2.运行代码后电脑端的动作(启动微信)。
上文我们在次了解了电脑(PC端)系统自带的应用程序,如:记事本、计算器等等,以及电脑(PC端)系统安装的应用程序,如QQ、微信等等。如何启动以及启动的几种方法,非常简单。
如果在电脑中这个应用程序已经启用了,我如何去启动这个已经启动的应用程序呢?
接下来我们了解和分享如何启动PC端已经启动的应用程序。
9.启动已启动的应用程序
上面的例子细心的你相信会发现都是程序没有运行起来的,那么想要启动已经运行起来的程序应该怎么做?
connect() 在要启动自动化应用程序时使用。 要指定已在运行的应用程序,您需要指定以下之一:
-
process:应用程序的进程ID,例如app = Application().connect(process=2341)
-
handle:应用程序窗口的窗口句柄,例如,app = Application().connect(handle=0x010f0c)
-
path:进程的可执行文件的路径(GetModuleFileNameEx用于查找每个进程的路径并与传入的值进行比较),例如:app = Application().connect(path=r"c:\windows\system32\notepad.exe")
-
或者指定窗口的参数的任意组合,这些都被传递给pywinauto.findwindows.find_elements() 函数。 例如:
app = Application().connect(title_re=".*Notepad", class_name="Notepad")
注意: 在使用connect*()之前,应用程序必须准备好(已启动)。
9.1通过进程号启动
这里以我们熟悉的大鹅QQ为例,进行演示!
1.如果我们想操作一个已经打开的应用程序,可以根据应用的进程号 PID 去操作,先查看应用进程PID,如下图所示:
2.通过connect()方法连接,process参数指定PID,PID是不固定的,每次启动都会变化。
9.1.1参考代码
from pywinauto.application import Application # 启动QQ app = Application(backend="uia").connect(process=19288) print(app)
9.1.2运行代码
1.运行代码,右键Run'Test',就可以看到控制台输出
9.2通过窗口句柄启动
通过窗口句柄的形式打开。问题来了?怎么查看窗口句柄?
1.通过Inspect.exe查看窗口句柄,如下图所示:
handle 句柄Inspect.exe查看是16进制的,每次启动也会变化。
因为进程PID和handle 句柄每次启动都会变化,所以我们应该用参数的组合。
2.还可以通过前面说的viewWizard工具进行查看(将放大镜拖到已启动的应用程序),通过下图可以看到窗口的句柄为133326,如下图所示:
9.2.2参考代码
from pywinauto.application import Application # 启动QQ app = Application(backend="uia").connect(process=10720,handle=0x1059C) print(app)
9.2.3运行代码
1.运行代码,右键Run'Test',就可以看到控制台输出
根据title 和 class 名称启动,根据title 和 class 名称连接也有缺点,必须窗口在桌面是开着的才行,挂后台的应用无法连上。
参考代码如下:
from pywinauto.application import Application # 启动QQ app = Application('uia').connect(title_re="QQ", class_name="TXGuiFoundation") print(app)
9.3应用程序路径
1.还可以根据exe应用程序的路径,路径是固定的,但是运行报错了。如下图所示:
9.3.2参考代码
from pywinauto.application import Application # 启动QQ app = Application('uia').connect(path=r'D:\QQ\Bin\QQScLauncher.exe') print(app)
9.4Desktop() 方法
还有另外一种连接方式, 通过Desktop
方法更简单。通过标题的方式打开。以计算器为例演示,有兴趣的自己可以尝试QQ。
9.4.1代码设计
9.4.2参考代码
import pywinauto # 通过窗口打开 app = pywinauto.Desktop() win = app['计算器'] print(win)
9.4.3运行代码
1.运行代码,右键Run'Test',就可以看到控制台输出
PC端自带和安装的应用程序启动,以及已启动的自带和安装的应用程序启动,你学会了吗,可参照上文自行实践~
已经知道如何将PC端的应用程序启动,以及如何连接已启动的应用程序,那么启动和连接上之后呢?不用说当然是操作应用程序了,怎么操作呢?
10.什么是窗口
窗口就是应用软件上面的工具栏,操作栏,弹出菜单,对话框等,这些基本上为windows软件应用比较多的地方。它通常具有带有标题栏、 最小化和最大化按钮以及其他标准 UI 元素的框架。 有兴趣的可以看一下这里有详细的介绍:什么是窗口 - Win32 apps | Microsoft Learn
11.如何获取窗口信息
这里介绍和分享两种方法:一种是通过工具获取,另一种是通过代码获取。
11.1方法1-工具
1.直接通过inspect工具进行获取窗口信息,启动记事本,可以通过inspect.exe 查看窗口信息。如下图所示:
2.主要看2个属性,窗口的标题,也就是左上角我们看到的还有一个是查看的class属性,只能通过工具查看。如下图所示:
11.2方法2-代码
通过print_control_identifiers()方法,print_control_identifiers()方法进行获取该应用下的所有窗口或组件内容(这里由于是Windows11系统用这个方法打开获取记事本窗口信息会报错)。查了好多资料以及去pywinauto社区查找都没找到解决办法,而且网上好多人都遇到了这个问题,因此这里用notepad++这款编辑器给小伙伴或者童鞋们来演示一下。其实用notepad++这款编辑器按照Windows10系统代码的写法也会报错: pywinauto.findwindows.ElementNotFoundError: {'class_name': 'Notepad', 'visible_only': True, 'backend': 'uia'} 。 但是自己想了一种解决办法:手动调用connect()
给予额外的时间缓冲,可能恰好让UI准备就绪。然后就可以了,试过记事本了,仍然不行。后边如想到解决办法,会写文章告知,要是没有解决方法,就不写相关文章,或者后期微软或者pywinauto社区会更新版本修复这一问题。
11.2.1代码设计
1.Windows10系统
from pywinauto import Application import time # 通过窗口打开 app = Application('uia').start("D:/software/Notepad/Notepad++/notepad++.exe") win = app['新文件 1 - Notepad++'] print(win) print(app.process) win.print_control_identifiers()
2.Windows11系统
from pywinauto import Application import time # 通过窗口打开 app = Application('uia').start("D:/software/Notepad/Notepad++/notepad++.exe") app = Application('uia').connect(class_name="Notepad++") win = app['新文件 1 - Notepad++'] print(win) print(app.process) win.print_control_identifiers()
11.3.3运行代码
1.运行代码,右键Run'Test',就可以看到控制台输出
2.运行代码后电脑端的动作(启动notepad++)
12.1如何选择窗口?
启动PC端的软件后,会出现软件的窗口界面,首先获取到窗口的信息,然后我们需要根据获取到的窗口信息连接到窗口,然后选择我们要测试的窗口,只有选择到要测试的窗口后,我们才能进行操作。如果你都无法选择到要测试窗口,何谈下一步的操作和自动化测试呢?因此接着介绍几种选择窗口的方法,其实之前的文章中已经或多或少的已经提及到,因为比较重要,因此在这里单独将其拎出来介绍和讲解一下。这里介绍和分享以下三种方法,通过选择notepad++的窗口,然后输入“rebort测试”。在Windows11系统演示,Windows10以下可以参考前边提到的代码,比Windows11简单多了。
12.1方法1-窗口属性值
通过窗口的属性值进行获取,代码如下:
win = app['属性值']
12.1.1代码设计
通过className属性值,代码如下:
from pywinauto import Application import time # 通过窗口打开 app = Application('uia').start("D:/software/Notepad/Notepad++/notepad++.exe") app = Application('uia').connect(class_name="Notepad++") # win = app['属性'] win = app['Notepad'] win.type_keys("rebort测试")
12.1.3运行代码
1.运行代码,右键Run'Test',就可以看到控制台输出
2.运行代码后电脑端的动作(启动notepad++,输入文本:rebort测试)
12.2方法2-窗口标题
通过窗口的标题获取,代码如下:
win = app['窗口的标题']
12.2.1代码设计
from pywinauto import Application import time # 通过窗口打开 app = Application('uia').start("D:/software/Notepad/Notepad++/notepad++.exe") app = Application('uia').connect(class_name="Notepad++") # win = app['窗口的标题'] win = app['新文件1 - Notepad++'] win.type_keys("rebort测试")
12.2.3运行代码
1.运行代码,右键Run'Test',就可以看到控制台输出
2.运行代码后电脑端的动作(启动notepad++,输入文本:rebort测试)
12.3方法3-窗口类名(不推荐)
这种点窗口名称的写法,只适合于一些英文的className属性,一些中文的标题,或者有特殊符号就会报语法错误。
通常用窗口的标题操作更稳定一些,用class名称有时候会报错找不到。
通过窗口的类名进行获取,代码如下:
win = app.类名
12.3.1代码设计
from pywinauto import Application import time # 通过窗口打开 app = Application('uia').start("D:/software/Notepad/Notepad++/notepad++.exe") app = Application('uia').connect(class_name="Notepad++") # win = app.类名 win = app.Notepad win.type_keys("rebort测试")
12.3.3运行代码
1.运行代码,右键Run'Test',就可以看到控制台输出
2.运行代码后电脑端的动作(启动notepad++,输入文本:rebort测试)
13.1遇到的问题
了解如何操作PC端应用程序的窗口、获取窗口信息、选择窗口,以及Windows10系统和Windows11系统代码的区别,主要是多了一段连接窗口的代码。如果缺少这一段代码就会报错: pywinauto.findwindows.ElementNotFoundError: {'best_match': '新文件 1 - Notepad++', 'backend': 'uia', 'process': 12464} 。如下图所示:
解决方法:上网和社区查了好多资料,都说没有解决办法,就自己试了一下,加上那段连接到窗口的代码就没有问题了,代码如下:
app = Application('uia').connect(class_name="Notepad++")
但是,试了记事本还是不可以,notepad++编辑器是可以了。真是奇怪了。
13.2环境变量
你如果想要像启动记事本一样启动notepad++,你只需要配置notepad++的环境变量即可,如何配置,就不在这里赘述了,自己网上找一下,特别多,都是一样套路,或者是换汤不换药。然后代码就可以按照如下方式写:
app = Application('uia').start("notepad++.exe")
PC端应用程序窗口信息和如何连接窗口对其进行操作的常用的几种方法。接着讲解和分享一下窗口的基本操作:最大化、最小化、恢复正常、关闭窗口、获取窗口状态和获取窗口坐标。以及窗口的其他打开方法和选择方法。
14.窗口的基本操作
窗口的一些基本操作方法:
-
最大化 :w.maximize()
-
最小化 :w.minimize()
-
恢复正常 :w.restore()
-
关闭窗口: w.close()
-
获取窗口状态: w.get_show_state() 返回1 最大化, 0正常,2最小化
-
获取窗口坐标: w.rectangle() 返回 (L35, T149, R1544, B913)
14.1代码设计
from pywinauto import Application import time # 通过窗口打开 app = Application('uia').start("D:/software/Notepad/Notepad++/notepad++.exe") app = Application('uia').connect(class_name="Notepad++") # win = app['窗口的标题'] win = app['新文件1 - Notepad++'] win.type_keys("rebort") time.sleep(2) win.maximize() # 最大化 tim