主要方案
-
pywinauto 、uiautomation 用于通过界面控件定位(这里更建议使用pywinauto )
-
pyautogui 模拟键鼠操作元素,主要用来弥补某些控件无法通过属性定位
-
opencv 可以通过图片定位某个坐标,或者用来做图片比对断言
-
值得注意的是,如果使用了坐标、图片则需要保证执行电脑的分辨率永不会变化
定位方式
- 窗口控件的属性,常用的就下面几种
- ID
- 类名
- XML路径语言
- CSS选择器
- Name
- 连接文本
- 标签名
- 通过图片定位(开发并不一定给所有的控件加属性),所以这个时候就需要用到通过图片去找到对应控件的坐标
- 直接通过坐标定位,主要用于复杂图片,图片识别度不够找不到图片的场景,或者控件的图片本身就是动态的,就通过屏幕的x、y坐标直接使用pyautogui去操作
控件定位工具
框架结构
UI自动化由于界面变动,维护成本较高,仍建议采用传统的PO模式
PO理念是自动化测试的一种设计思想,对应用的页面类进行分层设计,包括:元素管理层、操作管理层、业务逻辑管理。
简单理解就是:一个页面一个类,一个元素一个方法。
元素管理层:页面控件定位,通过元素属性、坐标、图片等方式定位,比如定位导入按钮 。
操作管理层:定位元素后,获取元素对象,进行操作,封装成页面动作,比如单击导入按钮。
业务逻辑层:多个页面动作组成的一个完整的业务逻辑,比如导入模型数据(单击导入按钮-单击选择模型数据-单击确定按钮)。
测试用例层:多个业务逻辑组成的一个完整的测试用例,比如导入模型数据-调整特征点-导出工程-验证工程内调整的特征点数据。
优点:尽可能方便后期维护,提高代码可读性,让用例只关注与业务逻辑。
按照以上结构封装,元素变了只用改元素,交互变了只用改交互,业务变了只用改业务,就不需要一边动就得改跟多的用例了
调用逻辑
脚本示例
-
启动应用程序:
- 使用 pywinauto 的 Application.start() 方法启动应用程序。
-
点击登录按钮:
- 使用 pywinauto 的 child_window() 方法找到登录按钮,并调用 click() 方法点击。
-
识别头像图片并点击:
-
使用 OpenCV 的模板匹配功能在屏幕上查找头像图片,并返回其中心坐标。
-
使用 pyautogui.click() 点击头像位置。
-
-
点击图片放大按钮:
- 使用 pyautogui.click() 点击指定的放大按钮坐标。
-
断言图片是否被放大:
-
使用 OpenCV 的模板匹配功能比较原始图片和放大后的图片。
-
如果放大后的图片匹配度更高,则断言图片已放大。
-
import time
import cv2
import numpy as np
import pyautogui
from pywinauto import Application
# 1. 启动应用程序
app_path = r"C:\Path\To\YourApp.exe" # 替换为你的应用程序路径
app = Application(backend="uia").start(app_path)
time.sleep(5) # 等待应用程序启动
# 2. 找到登录按钮并点击
window = app.window(title_re=".*你的应用标题.*") # 替换为你的应用窗口标题
login_button = window.child_window(title="登录", control_type="Button")
login_button.click()
print("点击登录按钮")
# 3. 使用 OpenCV 识别头像图片并点击
def find_image_on_screen(template_path, threshold=0.8):
"""在屏幕上查找图片并返回中心坐标"""
screenshot = pyautogui.screenshot()
screenshot = cv2.cvtColor(np.array(screenshot), cv2.COLOR_RGB2BGR)
template = cv2.imread(template_path, cv2.IMREAD_COLOR)
result = cv2.matchTemplate(screenshot, template, cv2.TM_CCOEFF_NORMED)
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)
if max_val >= threshold:
center_x = max_loc[0] + template.shape[1] // 2
center_y = max_loc[1] + template.shape[0] // 2
return center_x, center_y
else:
return None
# 查找头像图片
avatar_template_path = r"C:\Path\To\avatar_template.png" # 替换为头像图片路径
avatar_position = find_image_on_screen(avatar_template_path)
if avatar_position:
pyautogui.click(avatar_position)
print(f"点击头像位置: {
avatar_position}")
else:
print("未找到头像图片")
# 4. 使用 pyautogui 点击图片放大按钮
zoom_button_position = (100, 200) # 替换为放大按钮的坐标
pyautogui.click(zoom_button_position)
print(f"点击放大按钮位置: {
zoom_button_position}")
# 5. 使用 OpenCV 断言图片是否被放大
def assert_image_zoomed(original_template_path, zoomed_template_path, threshold=0.8):
"""断言图片是否被放大"""
original_template = cv2.imread(original_template_path, cv2.IMREAD_COLOR)
zoomed_template = cv2.imread(zoomed_template_path, cv2.IMREAD_COLOR)
screenshot = pyautogui.screenshot()
screenshot = cv2.cvtColor(np.array(screenshot), cv2.COLOR_RGB2BGR)
original_result = cv2.matchTemplate(screenshot, original_template, cv2.TM_CCOEFF_NORMED)
zoomed_result = cv2.matchTemplate(screenshot, zoomed_template, cv2.TM_CCOEFF_NORMED)
_, original_max_val, _, _ = cv2.minMaxLoc(original_result)
_, zoomed_max_val, _, _ = cv2.minMaxLoc(zoomed_result)
if zoomed_max_val > original_max_val and zoomed_max_val >= threshold:
print("图片已放大")
else:
print("图片未放大")
# 断言图片是否被放大
original_image_path = r"C:\Path\To\original_image.png" # 替换为原始图片路径
zoomed_image_path = r"C:\Path\To\zoomed_image.png" # 替换为放大后的图片路径
assert_image_zoomed(original_image_path, zoomed_image_path)
封装思路
- 软件启动方法封装为单例模式,启动后激活窗口,并检查窗口;配合conftest pytest.fixture使用
- 元素查找(包含通过属性、坐标、图片返回元素的坐标)
- 元素操作进行二次封装,封装成通用方法,比如传入坐标操作、传入图片路径操作、传入空间属性操作(包含常见的键盘鼠标操作)
- 断言封装成通过方法(包含通过属性、图片、进程检查)
- 结束软件封装成通过方法(包含通过点击软件界面退出、直接结束进程退出)
- 异常捕获,用例失败时截图,便于报告解读
文件目录参考
-pages 主要存放元素的定位、元素的操作、业务逻辑(比如一个上传动作、一个订单生成动作等)
-test_case 主要存放执行的用例,调用业务逻辑方法就可实现
-test_data 主要存放测试需要用到的数据
-static 存放通过图片定位的图片,通过图片断言的的图片
-commons 通用方法键鼠、cmd、断言、图片日志
-utils 通用方法 文件操作、JSON操作、yaml操作、通过ftp流转文件
-config 常用配置 账号密码、安装包路径、项目路径、项目文件路径(图片、日志、报告等)、应用的进程名称、窗口名称、等待时间等等