AirTest+Pytest框架+ddt实现UI自动化测试
使用AirTest(UI操作)+Pytest(测试框架)+ddt(数据驱动)实现UI自动化测试,核心是通过让同一测试逻辑适配多组输入数据,同时利用Pytest管理用例生命周期和报告,用AirTest完成设备交互和UI操作。
·
使用 AirTest(UI操作)+ Pytest(测试框架)+ ddt(数据驱动)实现UI自动化测试,核心是通过 数据驱动 让同一测试逻辑适配多组输入数据,同时利用 Pytest 管理用例生命周期和报告,用 AirTest 完成设备交互和UI操作。以下是可落地的完整步骤(以Android端APP为例):
一、核心工具分工
- AirTest:负责设备连接、APP启动/关闭、UI控件定位(点击、输入等操作)、截图日志记录。
- Pytest:提供用例组织(类/函数)、前置/后置操作(setup/teardown)、用例执行控制、测试报告生成。
- ddt:实现数据驱动,将多组测试数据注入同一测试方法,自动生成多条用例。
二、环境准备
1. 安装依赖库
# 核心库
pip install airtest # AirTest基础库(含设备连接、图像识别)
pip install pocoui # 控件识别库(poco,比图像识别更稳定)
pip install pytest # 测试框架
pip install ddt # 数据驱动库
# 辅助库(可选)
pip install pytest-html # 生成HTML测试报告
pip install pandas # 读取Excel/CSV数据(数据量大时用)
2. 设备连接(Android为例)
- 确保电脑安装
adb工具(配置环境变量),手机开启“开发者模式”和“USB调试”,连接电脑后执行:adb devices # 查看设备列表,确保设备状态为"device"(如:127.0.0.1:62001 device)
三、项目结构设计(清晰可维护)
ui_auto_test/
├── config/ # 配置文件(设备、APP信息)
│ └── settings.py # 设备连接串、APP包名等
├── data/ # 测试数据(多组输入)
│ └── test_data.py # 登录/注册等场景的数据
├── page/ # 页面对象(PO模式,分离控件和操作)
│ └── login_page.py # 登录页面的控件定位和操作方法
├── test_cases/ # 测试用例
│ └── test_login.py # 登录功能的测试逻辑
├── reports/ # 自动生成的测试报告
└── run.py # 用例执行入口
四、分步实现核心代码
1. 配置文件(config/settings.py)
定义设备连接信息和APP基础信息(避免硬编码):
# 设备连接字符串(格式:平台://adb服务器地址/设备ID)
# 从adb devices获取设备ID,如"127.0.0.1:62001"(夜神模拟器)
DEVICE_URL = "Android://127.0.0.1:5037/127.0.0.1:62001"
# 被测APP包名(通过adb shell dumpsys window | grep mCurrentFocus获取)
APP_PACKAGE = "com.example.myapp" # 替换为实际APP包名
2. 页面对象(page/login_page.py,PO模式)
将登录页面的控件定位和操作封装成方法(降低用例与控件的耦合):
from poco.drivers.android.uiautomation import AndroidUiautomationPoco
class LoginPage:
def __init__(self, poco: AndroidUiautomationPoco):
self.poco = poco # 传入poco控件驱动实例
# 定位控件(通过UI Inspector获取属性,优先用id/text)
@property
def username_input(self):
# 用户名输入框(替换为实际控件属性,如id="com.example.myapp:id/et_username")
return self.poco("com.example.myapp:id/et_username")
@property
def password_input(self):
# 密码输入框
return self.poco("com.example.myapp:id/et_password")
@property
def login_btn(self):
# 登录按钮
return self.poco("com.example.myapp:id/btn_login")
@property
def toast_msg(self):
# 提示信息(如"登录成功"、"密码错误")
return self.poco("com.example.myapp:id/tv_toast")
# 操作方法
def input_username(self, username: str):
"""输入用户名"""
self.username_input.wait_for_appearance(timeout=10) # 等待控件出现(超时10s)
self.username_input.clear_text() # 清空输入框
self.username_input.set_text(username)
def input_password(self, password: str):
"""输入密码"""
self.password_input.wait_for_appearance(timeout=10)
self.password_input.clear_text()
self.password_input.set_text(password)
def click_login(self):
"""点击登录按钮"""
self.login_btn.wait_for_appearance(timeout=10)
self.login_btn.click()
def get_toast(self) -> str:
"""获取提示信息文本"""
self.toast_msg.wait_for_appearance(timeout=10)
return self.toast_msg.get_text()
如何获取控件属性?
用 AirtestIDE 自带的 Poco辅助窗:打开IDE→连接设备→点击“Poco”按钮→选择对应APP→刷新控件树,即可查看控件的 id/text 等属性。
3. 测试数据(data/test_data.py)
用 ddt 支持的格式(列表/元组/字典)定义多组测试数据:
# 登录测试数据:(用户名, 密码, 预期提示信息)
login_data = [
("valid_user", "valid_pwd123", "登录成功"), # 正常登录
("valid_user", "wrong_pwd", "密码错误,请重新输入"), # 密码错误
("", "valid_pwd123", "用户名不能为空"), # 用户名空
("invalid_user", "any_pwd", "用户不存在") # 用户不存在
]
数据量大时? 可从Excel读取(需安装pandas):
import pandas as pd
def get_login_data_from_excel():
df = pd.read_excel("data/login_data.xlsx") # Excel列:username, password, expected
return df.values.tolist() # 转为[(u1,p1,e1), (u2,p2,e2)...]
4. 测试用例(test_cases/test_login.py)
结合 ddt 注入数据,用 Pytest 管理用例生命周期,调用页面对象完成测试:
import pytest
from ddt import ddt, data, unpack
from airtest.core.api import connect_device, start_app, stop_app, sleep
from poco.drivers.android.uiautomation import AndroidUiautomationPoco
from config.settings import DEVICE_URL, APP_PACKAGE
from data.test_data import login_data # 导入测试数据
from page.login_page import LoginPage # 导入登录页面对象
@ddt # 标记为数据驱动测试类
class TestLogin:
poco = None # 控件驱动实例
login_page = None # 登录页面对象
@classmethod
def setup_class(cls):
"""类级前置操作:连接设备、启动APP、初始化页面对象"""
# 1. 连接设备
connect_device(DEVICE_URL)
# 2. 初始化poco控件驱动
cls.poco = AndroidUiautomationPoco(use_airtest_input=True) # use_airtest_input:支持输入法输入
# 3. 启动APP
start_app(APP_PACKAGE)
sleep(5) # 等待APP启动完成
# 4. 初始化登录页面对象
cls.login_page = LoginPage(cls.poco)
@classmethod
def teardown_class(cls):
"""类级后置操作:关闭APP"""
stop_app(APP_PACKAGE)
sleep(2)
def setup_method(self):
"""方法级前置:进入登录页面(每个用例前执行)"""
# 假设首页有"我的"按钮,点击后进入登录页(根据实际APP流程修改)
self.poco(text="我的").click()
sleep(2)
def teardown_method(self):
"""方法级后置:返回首页(避免用例间污染)"""
self.poco("com.example.myapp:id/iv_back").click() # 返回按钮
sleep(2)
@data(*login_data) # 注入测试数据(*解包列表)
@unpack # 解包元组为单个参数(username, password, expected)
def test_login(self, username, password, expected):
"""登录测试用例:输入→点击→断言结果"""
# 1. 输入用户名和密码
self.login_page.input_username(username)
self.login_page.input_password(password)
# 2. 点击登录
self.login_page.click_login()
sleep(3) # 等待结果
# 3. 断言实际提示是否符合预期
actual = self.login_page.get_toast()
assert actual == expected, f"用例失败:输入[{username},{password}],预期[{expected}],实际[{actual}]"
5. 执行入口(run.py)
用 Pytest 运行用例并生成报告:
import pytest
if __name__ == "__main__":
# 运行test_cases目录下的所有用例,生成HTML报告到reports目录
pytest.main([
"test_cases/", # 用例目录
"-v", # 显示详细日志
"--html=reports/login_test_report.html", # 生成pytest-html报告
"--self-contained-html", # 报告独立(不依赖外部文件)
"--maxfail=3" # 失败3条用例后停止执行(可选)
])
五、运行与结果查看
- 执行测试:运行
run.py,控制台会输出用例执行进度(如collected 4 items表示4条用例)。 - 查看报告:打开
reports/login_test_report.html,可看到每条用例的执行结果(通过/失败)、失败原因(含断言信息)。 - AirTest日志:默认在
test_cases目录生成log文件夹,包含每条操作的截图和日志,便于调试失败用例。
六、关键优化技巧
-
控件定位稳定性:
- 优先用
id(唯一且不变),其次用text(需确保文字固定),避免用坐标(受屏幕分辨率影响)。 - 用
wait_for_appearance(timeout=10)替代固定sleep(),减少等待时间的同时避免控件未加载导致的失败。
- 优先用
-
数据驱动扩展:
- 支持从JSON/CSV/数据库读取数据,只需在
data/test_data.py中修改数据读取逻辑。
- 支持从JSON/CSV/数据库读取数据,只需在
-
报告增强:
- 结合
pytest-rerunfailures实现失败重试(pip install pytest-rerunfailures,运行时加--reruns 2)。 - 用
AirTest的simple_report生成操作截图报告(在teardown_class中添加:simple_report(__file__, output="reports/airtest_report.html"))。
- 结合
通过这套框架,既能利用 AirTest 高效操作UI,又能通过 ddt 覆盖多场景,同时用 Pytest 简化用例管理,适合移动端APP的UI自动化测试。核心是分离数据、控件、逻辑,让用例更易维护。
更多推荐



所有评论(0)