GUI自动化测试
一、pywinauto
Pywinauto是一款基于Python的跨平台GUI自动化库,专门针对Windows桌面应用程序设计,核心能力在于模拟用户交互行为实现对窗口、对话框及内部空间的精准定位与操作,基于Web的应用程序以及基于自定义渲染引擎的应用程序无法使用Pywinauto实现GUI自动化测试
1. pywinauto常见操作
1.1 打开程序
1.1.1 打开应用程序
start(self, cmd_line......)
cmd_line:是启动应用程序的命令行字符串,必须包含应用程序的路径和名称,还可以包含启动参数
1.1.2 连接已经打开的应用程序
connect(self, **kwargs)
部分参数详解:
process:目标的进程ID
handle:目标的窗口句柄
注意:连接到已在运行的进程,该动作仅根据一个参数执行
#打开应⽤程序
app = Application(backend="uia").start("D:\software\Sublime Text
3\sublime_text.exe")
#连接已经打开的应⽤程序
#通过pid连接
#获取应⽤程序对应的pid
#app = Application(backend="uia").connect(process=app.process)
app = Application(backend="uia").connect(process=31528)
#通过句柄连接
app = Application(backend="uia").connect(handle=65552)
1.2 定位窗口
方法一:通过高级pywinauto API提供的window方法来定位
app.window(title='',...)
参数说明(参数可以组合使用):
- title:文本为指定值的元素
- title_re:文本匹配指定正则表达式的元素
- best_match:标题与指定值相似的元素
- class_name:窗口类为指定值的元素
- class_name_re:类名匹配指定正则表达式的元素
#start参数换成Sublime Text的⽬标路径
app = Application(backend='uia').start("D:\software\Sublime Text
3\sublime_text.exe")
win = app.window(title_re='.*Sublime Text.*')
#title--精确匹配
win = app.window(title="untitled • - Sublime Text (UNREGISTERED)")
#title_re-正则匹配
win = app.window(title_re=".*Sublime.*")
#best_match-模糊匹配
win = app.window(best_match="untitled • - Sublime Text")
#class_name--精确匹配
win = app.window(class_name="PX_WINDOW_CLASS")
#class_name--正则匹配
win = app.window(class_name_re=".*WINDOW_CLASS")
win.print_control_identifiers()
print_control_identifiers()方法用于打印窗口及其子控件的标识符信息,帮助用户识别控件
输出的内容:
- 控件的类名、标题、位置(上下左右的边界的坐标值、控制类型等信息)
- 每个控件的“best match”名称列表,这些名称可以用于引用控件
Dialog - 'untitled • - Sublime Text (UNREGISTERED)' (L806, T81, R1811,
B1007)
['Dialog', 'untitled • - Sublime Text (UNREGISTERED)Dialog', 'untitled • -
Sublime Text (UNREGISTERED)']
child_window(title="untitled • - Sublime Text (UNREGISTERED)",
control_type="Window")
|
| ScrollBar - '' (L815, T44, R915, B54)
| ['ScrollBar', 'ScrollBar0', 'ScrollBar1']
| |
| | Button - '左移⼀列' (L0, T0, R0, B0)
| | ['左移⼀列', 'Button', '左移⼀列Button', 'Button0', 'Button1']
| | child_window(title="左移⼀列", auto_id="UpButton",
control_type="Button")
| |
| | Thumb - '位置' (L0, T0, R0, B0)
| | ['Thumb', '位置', '位置Thumb', 'Thumb0', 'Thumb1', '位置0', '位置1',
'位置Thumb0', '位置Thumb1']
| | child_window(title="位置", auto_id="ScrollbarThumb",
control_type="Thumb")
| |
| | Button - '右移⼀列' (L0, T0, R0, B0)
| | ['右移⼀列Button', '右移⼀列', 'Button2']
| | child_window(title="右移⼀列", auto_id="DownButton",
control_type="Button")
|
| ScrollBar - '' (L715, T144, R725, B244)
| ['ScrollBar2']
| |
| | Button - '上⼀⾏' (L0, T0, R0, B0)
| | ['上⼀⾏', '上⼀⾏Button', 'Button3']
| | child_window(title="上⼀⾏", auto_id="UpButton",
control_type="Button")
| |
| | Thumb - '位置' (L0, T0, R0, B0)
| | ['Thumb2', '位置2', '位置Thumb2']
| | child_window(title="位置", auto_id="ScrollbarThumb",
control_type="Thumb")
| |
| | Button - '下⼀⾏' (L0, T0, R0, B0)
| | ['下⼀⾏', '下⼀⾏Button', 'Button4']
| | child_window(title="下⼀⾏", auto_id="DownButton",
control_type="Button")
|
| TitleBar - '' (L835, T84, R1802, B119)
| ['TitleBar']
| |
| | Menu - '系统' (L652, T72, R674, B94)
| | ['Menu', '系统Menu', '系统', '系统0', '系统1', 'Menu0', 'Menu1']
| | child_window(title="系统", auto_id="MenuBar", control_type="MenuBar")
| | |
| | | MenuItem - '系统' (L652, T72, R674, B94)
| | | ['系统2', 'MenuItem', '系统MenuItem', 'MenuItem0', 'MenuItem1']
| | | child_window(title="系统", control_type="MenuItem")
| |
| | Button - '最⼩化' (L1624, T82, R1684, B119)
| | ['最⼩化Button', '最⼩化', 'Button5']
| | child_window(title="最⼩化", control_type="Button")
| |
| | Button - '最⼤化' (L1684, T82, R1743, B119)
| | ['最⼤化', 'Button6', '最⼤化Button']
| | child_window(title="最⼤化", control_type="Button")
......
......
方法二:通过动态解析对象属性定位
win = app.Dialog
#上⾯写法等价于
win = app.window(best_match='Dialog')
Python通过动态解析对象属性简化了创建窗口规范,但是存在不具备唯一性和稳定性的问题
1.3 窗口操作
| 方法 | 功能描述 | 返回值 |
| close() | 关闭窗口 | 无返回值 |
| maximize() | 最大化窗口 | |
| minimize() | 最小化窗口 | |
| restore() | 窗口恢复正常大小 | |
| get_show_state() | 获取窗口的显示状态 | 返回一个整数值表示窗口的状态 |
| is_dialog() | 检查控件是否是顶级窗口 | 返回布尔值 |
| is_maximized() | 检查窗口是否处于最大化状态 | 返回布尔值 |
| is_minimized() | 检查窗口是否处于最小化状态 | 返回布尔值 |
| is_normal() | 检查窗口是否处于原始状态(未大未小) | 返回布尔值 |
1.4 定位控件
1.4.1 控件分类
打开UISpy,通过ControlType可查看所定位到控件的类型,常见控件实例:
| 分类 | 空间名称 | 说明 |
|
窗⼝与对话框
|
对话框 (Dialog)
|
⽤于与⽤⼾交互,如显⽰警告、确认操作或输⼊信息。
|
|
窗格 (Pane)
|
通常作为窗⼝的⼀部分,⽤于显⽰特定内容或功能。
| |
|
输⼊控件
|
按钮 (Button)
|
⽤于触发操作,如点击按钮执⾏某个功能。
|
|
编辑栏 (Edit)
|
⽤于输⼊或编辑⽂本,⽀持多⾏或单⾏输⼊。
| |
|
组合框 (ComboBox)
|
结合⽂本框和列表框,⽤⼾可以选择预定义选项或输入自定义值。
| |
|
列表框 (ListBox)
|
⽤于显⽰可选择的项⽬列表,⽀持单选或多选。
| |
|
菜单控件
|
菜单 (Menu)
|
⽤于提供功能选项,通常位于窗⼝顶部。
|
|
菜单项 (MenuItem)
|
菜单中的具体选项,点击后执⾏对应功能。
| |
|
弹出菜单 (PopupMenu)
|
右键单击时弹出的菜单,⽤于快速访问常⽤功能。
| |
|
列表显⽰控件
|
列表显⽰控件 (ListView)
|
⽤于以表格形式显⽰数据,⽀持多列显⽰和排序。
|
|
容器控件
|
组框 (GroupBox)
|
⽤于对控件进⾏分组,提⾼界⾯的可读性和组织性。
|
|
选择控件
|
复选框 (CheckBox)
|
⽤于多选操作,⽤⼾可以勾选多个选项。
|
|
单选框 (RadioButton)
|
⽤于互斥选择,⽤⼾只能选择⼀个选项。
| |
|
显⽰控件
|
状态栏 (StatusBar)
|
通常位于窗⼝底部,⽤于显⽰应⽤状态或提⽰信息。
|
|
静态内容 (Static)
|
⽤于显⽰静态⽂本或图像,通常不可编辑。
| |
|
导航控件
|
树状视图 (Tree View)
|
⽤于展⽰分层数据,如⽂件夹结构或组织结构。
|
|
选项卡控件 (TabControl)
|
⽤于在有限空间内组织多个⻚⾯或选项卡,每个选项卡可以包含不同的内容。
| |
|
⼯具控件
|
⼯具栏 (Toolbar)
|
⽤于放置常⽤按钮或⼯具,⽅便⽤⼾快速操作。
|
|
⼯具提⽰ (ToolTips)
|
当⿏标悬停在控件上时显⽰提⽰信息,帮助⽤⼾理解控件功能。
| |
|
头部内容
|
头部内容 (Header)
|
通常⽤于显⽰标题或表头信息,如表格的列标题。
|
定位控件,需要借助前面学到的print_control_identifiers()方法,打印窗口及其子空间的标识符信息
1.4.2 动态解析
基于best_match标题来定位
from pywinauto import Application
# 创建Application对象,连接到正在运⾏的Sublime Text进程
app = Application(backend='uia').connect(process=38544)
# 获取与Sublime Text相关的窗⼝对象,使⽤正则表达式匹配窗⼝标题
win = app.window(title_re='.*Sublime Text.*')
# 等待窗⼝变为可⻅状态,确保窗⼝已经加载完成
win.wait("visible")
# 通过窗⼝的标题获取菜单对象
menu = win['应⽤程序']
# 打印菜单项的列表,查看菜单中包含的所有选项
print(menu.items())
1.4.3 child_window
child_window()和定位窗口的方法window()参数一样,可以通过标题或者类名进行精确匹配、模糊匹配等,额外拓展几个进行空间定位时需要用到的参数:
| 属性名称 | 描述 |
| control_type |
具有此控件类型的元素(字符串) |
| auto_id | 具有此自动化ID的元素 |
| found_index | 返回的已过滤子元素的索引,下标从0开始 |
from pywinauto import Application
# 创建Application对象,连接到正在运⾏的Sublime Text进程
app = Application(backend='uia').connect(process=38544)
# 获取与Sublime Text相关的窗⼝对象,使⽤正则表达式匹配窗⼝标题
win = app.window(title_re='.*Sublime Text.*')
# 等待窗⼝变为可⻅状态,确保窗⼝已经加载完成
win.wait("visible")
# 通过控件的属性(标题、⾃动ID和控件类型)定位菜单栏对象
menu = win.child_window(title="应⽤程序", auto_id="MenuBar",
control_type="MenuBar")
# 打印菜单项的列表,查看菜单中包含的所有选项
print(menu.items())
若存在多个控件的auto_id相同,可通过添加索引参数来定位
from pywinauto import Application
# 创建Application对象,连接到正在运⾏的Sublime Text进程
app = Application(backend='uia').connect(process=38544)
# 获取与Sublime Text相关的窗⼝对象,使⽤正则表达式匹配窗⼝标题
win = app.window(title_re='.*Sublime Text.*')
# 等待窗⼝变为可⻅状态,确保窗⼝已经加载完成
win.wait("visible")
#存在相同的auto_id和control_type,使⽤found_index来定位
menu = win.child_window(auto_id="MenuBar",
control_type="MenuBar",found_index=1)
print(menu.texts())
注意,若元素值为空,或不是唯一的情况下,都会导致定位失败
控件之间存在父子关系,可通过children()方法获取控件的子类,也可以通过parent()方法获取控件的父类
1.5 等待
GUI应用程序行为通常不稳定,脚本需要等待,直到出现新窗口或关闭现有窗口等
1.5.1 wait/wait_not()
wait(self, wait_for, timeout=None, retry_interval=None)
wait_not(self, wait_for_not, timeout=None, retry_interval=None)
- wait_for:表示选择的窗口状态
- exists:表示窗口是一个有效的句柄
- visible:表示窗口不隐藏,可见
- enable:表示窗口未被禁用,可操作
- ready:表示窗口可见且已启用
- active:表示窗口处于活动状态
- timeout:表示超时
- retry_interval:表示重试时间间隔
1.5.2 wait_until
等待满足某个条件
wait_until(timeout,retry_interval,func,value=True,op=operator.eq,*args, **kwargs)
- Timeout:超时时间
- retry_interval:重试时间
- func:执行的函数
- value:比较的值
i=0
def work():
global i
i += 1
print("当前i的值为",i)
return i
#等待work返回的结果为5,继续往下执⾏
wait_until(10,1,work,5)
print("等待通过")
1.6 控件的操作
1.6.1 点击
- click_input():模拟鼠标左键单击操作
- right_click_input():模拟鼠标右键单机操作
- double_click_input():模拟鼠标右键双击操作
1.6.2 文本
- texts():用于获取窗口或控件中的所有文本内容,返回一个列表,其中每个元素是一个字符串,表示窗口或空间中的某个文本片段
- window_text():用于获取窗口或控件的主文本内容,通常是指窗口的标题或主要显示的文本,返回一个字符串
1.7 鼠标操作
示例:鼠标点击
from pywinauto import Application
app = Application(backend='uia').connect(process=32936)
win = app.window(title_re='.*Sublime Text.*')
win.wait("visible")
#通过⿏标点击
mouse.click(coords=(1043,130))
示例:拖动滚动条
from pywinauto import Application
# app = Application(backend='uia').start("mspaint.exe")
app = Application(backend='uia').connect(process=28372)
win = app.window(title="⽆标题 - 画图")
win.wait('visible')
#定位滚动条
right_ScrollBar = win.child_window(title="垂直滚动条",
auto_id="NonClientVerticalScrollBar", control_type="ScrollBar")
right_ScrollBar.wait('visible')
#获取滚动条中间位置
mid = right_ScrollBar.rectangle().mid_point()
#从中间位置下拉
mouse.scroll(coords=(mid.x,mid.y),wheel_dist=500)
- rectangle():获取元素对应坐标,返回矩形尺寸,具有top、left、right、bottom属性
- mid_point():获取元素中间位置坐标,返回类型为元组,元组中两个整数分别是X\Y
from pywinauto import Application
# app = Application(backend='uia').start("mspaint.exe")
app = Application(backend='uia').connect(process=28372)
win = app.window(title="⽆标题 - 画图")
win.wait('visible')
#定位滚动条
right_ScrollBar = win.child_window(title="垂直滚动条",
auto_id="NonClientVerticalScrollBar", control_type="ScrollBar")
right_ScrollBar.wait('visible')
#获取滚动条中间位置
mid = right_ScrollBar.rectangle().mid_point()
#从中间位置下拉
mouse.scroll(coords=(mid.x,mid.y),wheel_dist=500)
mouse模块常见操作:
| 方法 | 功能描述 |
|
mouse.click(button='left', coords=(0, 0))
|
单击指定的坐标
|
|
mouse.scroll(coords=(0, 0), wheel_dist=1)
|
滚动⿏标滚轮,wheel_dist表⽰滚动的 距离,⼤于0是向上滚动,⼩于0是向下滑动。
|
|
mouse.double_click(button='left',coords=(0, 0))
|
双击指定的坐标
|
|
mouse.right_click(coords=(0, 0))
|
右键单击指定的坐标
|
|
mouse.move(coords=(0, 0))
|
移动⿏标到指定坐标
|
|
mouse.wheel_click(coords=(0, 0))
|
⿏标中键单击指定的坐标
|
|
mouse.press(button='left', coords=(0, 0))
|
按下⿏标按钮
|
|
mouse.release(button='left', coords=(0, 0))
|
释放⿏标按钮
|
# app = Application(backend="uia").start("C:\Program Files
(x86)\ByteDance\douyin\douyin.exe")
app = Application(backend="uia").connect(process=14488)
win = app['抖⾳']
win.wait("visible")
#双击点赞
for i in range(0,3):
mid = win.rectangle().mid_point()
mouse.double_click(coords=(mid.x,mid.y))
time.sleep(1)
#滑到下⼀个视频
mouse.scroll(coords=(mid.x, mid.y), wheel_dist=-500)
1.8 键盘操作
keyborad.send_keys()是一个通用的键盘输入方法,可以直接将案件序列发送到当前具有焦点的窗口,但在实际的自动化场景中,通常需要针对特定的控件进行精确的输入操作,为了满足这种需求,pywinauto引入了type_keys方法,这是控件对象的专属方法,可以直接作用于指定的控件
type_keys(
keys, # 要输⼊的键序列,可以是普通字符、特殊键或组合键
pause = None, #每次按键后的延迟时间(秒)
with_spaces = False, # 如果为 True,则会在输⼊的字符串中保留空格。
with_newlines = False, #如果为 True,则会在输⼊的字符串中保留换⾏符。
...
)
1.8.1 输入文本
直接输入文本:
from pywinauto import Application
app = Application(backend='uia').connect(process=32936)
win = app.window(title_re='.*Sublime Text.*')
win.wait("visible")
#输⼊⽂本内容
win.123("------type_keys------")
#保留换⾏符
win.type_keys("------type_keys------\n",with_newlines=True)
#保留空格
win.type_keys(" -----type keys----",with_spaces=True)
#延迟输⼊,避免输⼊过快导致内容不完整
win.type_keys("⼀⼆三四五六七",with_spaces=True)
1.8.2 按键
使用{}格式表示虚拟键码:
from pywinauto import Application
app = Application(backend='uia').connect(process=32936)
win = app.window(title_re='.*Sublime Text.*')
win.wait("visible")
#发送⽂本和回⻋
win.type_keys("Hello World{ENTER}",with_spaces=True)
win.type_keys("Hello bit",with_spaces=True)
可以为特殊按键指定重复次数,{ENTER 2}表示按两次ENTER键
使用{}包裹特殊字符,(如{+},{^})以避免被识别为修饰符
win.type_keys("1+2=3") # 错误:`+` 会被识别为 Shift
win.type_keys("1{+}2=3") # 正确
1.9 菜单控件的操作
1.9.1 items()
返回对话框的菜单项,如果没有则返回空列表
from pywinauto import Application
app = Application(backend='uia').connect(process=38544)
win = app.window(title_re='.*Sublime Text.*')
win.wait("visible")
menu = win['应⽤程序']
print(menu.items())
1.9.2 Item_by_index()
语法:item_by_index(idx)查找索引指定的菜单项,idx代表索引(从0开始)
from pywinauto import Application
app = Application(backend='uia').connect(process=38544)
win = app.window(title_re='.*Sublime Text.*')
win.wait("visible")
menu = win['应⽤程序']
menu.item_by_index(0)
1.9.3 item_by_path()
语法:item_by_path(path, exact=false)用户查找路径指定的菜单项
- path:用于指定要选择的菜单项路径
- exact:设置为true,则要求菜单项名称与路径中的名称完全匹配;为false,则允许模糊匹配
1.9.4 menu_select()
语法:menu_select(path, exact=false)
1.10 列表控件的操作
1.10.1 get_items()
获取列表视图中的所有项目
from pywinauto import Application
# app = Application(backend='uia').start("explorer.exe")
app = Application(backend='uia').connect(process=9628)
win = app.window(title_re='.*⽂件资源管理器')
win.wait('visible')
data_list = win.child_window(auto_id="HomeListView", control_type="List")
#打印列表项
print("items:",data_list.get_items())
1.10.2 item_count()
列表视图中的项数
from pywinauto import Application
# app = Application(backend='uia').start("explorer.exe")
app = Application(backend='uia').connect(process=9628)
win = app.window(title_re='.*⽂件资源管理器')
win.wait('visible')
data_list = win.child_window(auto_id="HomeListView", control_type="List")
#打印列表项数量
print("items:",data_list.item_count())
1.10.3 get_item()
返回列表视图中的指定项目
参数:row(可以是行的索引)
from pywinauto import Application
# app = Application(backend='uia').start("explorer.exe")
app = Application(backend='uia').connect(process=9628)
win = app.window(title_re='.*⽂件资源管理器')
win.wait('visible')
data_list = win.child_window(auto_id="HomeListView", control_type="List")
#获取列表中第⼀项
print("items:",data_list.get_item(row=0))
更多推荐

所有评论(0)