本UI自动化测试框架采用Page Object设计模式,结合Airtest图像识别和Poco
UI自动化技术,实现稳定可靠的自动化测试。
🛠️ 技术栈:
- Airtest - 跨平台UI自动化测试框架(图像识别)
- Poco - UI自动化引擎(控件识别)
- Pytest - Python测试框架
- Allure - 测试报告生成工具
- Python 3.12+ - 编程语言
Airtest框架官方文档:https://airtest.doc.io.netease.com/tutorial/0_automated_testing/
一、环境安装
1、安装本地airtest Python环境
plain
pip install pytest
pip install airtest==1.4.3
pip install --no-compile pocoui==1.0.94
pip install allure-pytest==2.16.0
同时安装Airtest IDE 软件,方便元素定位与图片截取
2、安装Allure Commandline
目的:把 allure-pytest 收集到的测试结果,渲染成可视化的 HTML 测试报告,并支持本地预览、CI 归档和分享
Windows (使用Scoop):
plain
# 先安装 Scoop(如果尚未安装)
Set-ExecutionPolicy RemoteSigned -Scope CurrentUser
irm get.scoop.sh | iex
scoop install allure
二、搭建测试框架
这里以一个核心功能为快速扫描病毒的安卓TV应用项目为例,来搭建UI自动化测试框架
1、框架结构
plain
UI/
├── config/ # 配置模块
│ ├── app_config.py # 应用配置(包名、超时时间等)
│ └── device_config.py # 设备配置
├── actions/ # 基础操作层
│ ├── base_action.py # Page基类(通用操作方法)
│ └── tv_remote.py # TV遥控器操作封装
├── pages/ # 页面对象层
│ ├── home_page.py # 首页页面对象
│ └── problem_area_page.py # 问题区域页面对象
├── testcases/ # 测试用例层
│ └── test_quick_scan.py # 快速扫描测试用例
├── utils/ # 工具类模块
│ ├── assert_utils.py # 断言工具(集成Allure截图)
│ ├── device_utils.py # 设备连接工具
│ ├── poco_elements.py # Poco元素定位器常量
│ └── poco_manager.py # Poco单例管理器
├── resources/images/ # 图片资源(截图模板)
├── screenshots/failures/ # 断言失败截图
├── reports/ # 测试报告目录(Git忽略)
├── conftest.py # Pytest配置文件(Fixture定义)
├── pytest.ini # Pytest配置
├── run.py # 测试运行脚本
└── view_report.py # 报告查看脚本
2、 核心特性
1. Page Object模式
- pages/ - 封装页面元素和操作
- actions/base_action.py - 提供通用操作方法
- 提高代码复用性和可维护性
2. Poco单例管理
- utils/poco_manager.py - 单例模式管理Poco实例
- 避免重复创建,提高性能
- 自动处理设备重连
3. 智能断言
- utils/assert_utils.py - 统一断言入口
- 支持Poco文本断言和图片断言
- 失败自动截图
4. TV遥控器封装
- actions/tv_remote.py - Android TV遥控器操作
- 支持方向键、确认键、返回键等
- 提供便捷的导航方法
- 可与Poco混合使用
5. Allure报告集成
- 详细的测试步骤展示
- 测试分类和严重程度标记
- 美观的HTML报告界面
3、 常用命令
bash
# 安装依赖
pip install -r requirements.txt
# 运行所有测试
python run.py
# 运行指定测试文件
pytest testcases/test_quick_scan.py -v
# 运行带标记的测试
pytest -m smoke # 冒烟测试
pytest -m critical # 关键测试
# 生成Allure报告
allure generate reports/allure_results -o reports/allure_report --clean
# 查看报告
allure open reports/allure_report
# 清理缓存
pytest --cache-clear
4、测试报告
Allure报告包含:
- ✅ 测试执行概览
- ✅ 测试步骤详情
- ✅ 失败用例分析
- ✅ 测试趋势图表
- ✅ 环境信息
三、测试用例实例
1、两套核心断言技术:Airtest vs Poco
这是整个框架最关键的区分。理解了它们的差异,就知道什么场景用什么。
| 维度 | Airtest(图像识别) | Poco(UI 控件树) |
|---|---|---|
| 原理 | OpenCV 模板匹配,在屏幕截图中找图 | 读取 Android UI 层级树,按属性定位元素 |
| 定位方式 | 图片模板 Template(img) |
text / resourceId / textMatches |
| 稳定性 | 受分辨率、主题、动画影响 | 只要控件属性不变就稳定 |
| 速度 | 慢(需截屏+模板匹配) | 快(直接查控件树) |
| 能断言什么 | 图片是否存在(图标、logo) | 文本、按钮、列表项等有属性的控件 |
统一断言入口 assert_element,封装三种断言方法
poco_selector(最推荐) > text > img(兜底)
python
def assert_element(
img=None,
text=None,
poco_selector=None,
timeout=10,
interval=1,
msg="元素不存在",
snapshot_on_fail=True,
test_name=None
):
"""统一断言入口"""
_log(f"开始断言: text={text}, img={img}, selector={poco_selector}")
# 优先使用Poco断言
if text or poco_selector:
poco = PocoManager.get_instance()
start = time.time()
while time.time() - start <= timeout:
try:
if text and poco(text=text).exists():
_log("Poco断言成功")
return True
# poco_selector 为字典选择器(如 {"text": "xx", "name": "yy"}),
# 必须用 ** 解包为关键字参数;整体作为位置参数传入会抛异常并被下方 except 静默吞掉
if poco_selector and poco(**poco_selector).exists():
_log("Poco断言成功")
return True
except Exception:
pass
time.sleep(interval)
_log("Poco未命中")
# fallback到图片断言
if img:
try:
# 等待图片出现
pos = wait(Template(img), timeout=timeout)
if pos:
_log("图片断言成功")
return pos
except Exception:
_log("图片断言未命中")
# 失败处理
if snapshot_on_fail:
file = _screenshot("assert_fail", test_name)
_log(f"截图: {file}")
raise AssertionError(f"{msg} | text={text} | img={img}")
2、三种用法及适用场景
① Poco 选择器断言(poco_selector)--- 最推荐
适用于:动态文本、需要多条件组合定位的元素。
python
# 实例1:威胁数量文案动态变化 --- "There are currently 1 threats"
# 数量会变,精确匹配不到,用正则匹配前缀
assert_element(
poco_selector={"textMatches": PocoElements.THREAT_COUNT_TEXT_REGEX},
timeout=AppConfig.TIMEOUT_MEDIUM,
msg="威胁数量文本未显示"
)
# 实例2:多属性组合定位(如有必要)
assert_element(
poco_selector={"text": "Scan Center", "name": "nav_item"},
timeout=AppConfig.TIMEOUT_LONG,
msg="导航项不存在"
)
② 文本断言(text)--- 静态文案
适用于:文案固定不变的按钮、标题、状态描述。
python
# 实例:扫描成功后的固定状态描述
assert_element(
text=PocoElements.SUCCESS_DESCRIPTION, # "There are no threats at the moment"
timeout=AppConfig.TIMEOUT_SCAN,
msg="扫描成功描述未显示"
)
③ 图片断言(img)--- 无文字/无 ID 的图标
适用于:只有图片、没有文字和 ID 的场景(如盾牌对勾图标、警告图标)。
python
# 实例:扫描结果页的盾牌对勾图标
assert_element(
img="resources/images/threat.png",
timeout=AppConfig.TIMEOUT_VERY_LONG,
msg="未检测到威胁警告图标"
)
3、写一条用例的完整流程
以"快速扫描成功"为例,从零到一。
步骤 1:准备图像模板(仅当需要 img 断言时)
把要断言的图标截图,放到 resources/images/,命名语义化:
resources/images/
├── threat.png # 威胁页警告图标
├── home_quick_scan_btn.png
└── quick_scan_sucess.png
步骤 2:在 PocoElements 定义文案常量
utils/poco_elements.py:
python
class PocoElements:
SUCCESS_DESCRIPTION = "There are no threats at the moment"
UNINSTALL_BUTTON_TEXT = "Uninstall"
# 动态文案用正则
THREAT_COUNT_TEXT_REGEX = "There are currently.*"
步骤 3:在 Page 类封装操作
pages/home_page.py:
python
class HomePage(BaseAction):
def click_quick_scan(self):
self.go_to_scan_center()
self.click_by_text(PocoElements.SCAN_CENTER_QUICK_SCAN_TEXT, timeout=AppConfig.TIMEOUT_LONG)
步骤 4:写测试用例
testcases/test_quick_scan_with_data.py:
python
@allure.title("快速扫描成功")
def test_quick_scan_success(self):
# 1. 数据准备
TestDataUtils.clean_virus_from_scan()
# 2. 执行操作
self.home_page.click_quick_scan()
# 3. 断言(固定文案 → text)
assert_element(
text=PocoElements.SUCCESS_DESCRIPTION,
timeout=AppConfig.TIMEOUT_SCAN,
msg="扫描结果未显示安全状态"
)
四、快速运行
1、配置Android设备
-
启用开发者选项和USB调试
-
连接设备到电脑
-
验证连接:
bashadb devices
2、 运行测试
使用run.py(推荐)
bash
python run.py
此命令会:
- ✅ 执行所有测试用例
- ✅ 生成Allure测试结果数据
- ✅ 自动生成Allure HTML报告
3、查看报告
使用view_report.py
bash
python view_report.py
会自动打开最新的Allure报告。