使用 Airtest 进行 Flutter/Unity UI 自动化测试终极指南
一、 核心原理:为什么 Airtest 能行?
要理解如何用,先要明白其原理。Airtest 采取了“两条腿走路”的策略,这正是它能通吃各种UI技术的关键。
- 第一条腿:基于图像识别 (Airtest Core)
-
- 原理: Airtest 将你的手机屏幕看作一张大图片。你提供一个小的目标图片(如一个按钮的截图),Airtest 就在当前屏幕这张大图片中去寻找与目标图片最相似的区域。
- 优点: “所见即所得”,无视底层技术实现。无论是 Flutter、Unity、原生应用、小程序,甚至是远程桌面,只要它能在屏幕上显示出来,Airtest 就能“看到”并操作它。这是测试 Flutter 和 Unity 的保底方案和通用基础。
- 缺点: 对UI变化敏感(颜色、大小、形状改变会导致识别失败)、性能开销相对较大、无法获取控件内部属性(如文本内容)。
- 第二条腿:基于UI控件树 (Poco Framework)
-
- 原理: Poco 像一个“翻译官”,它通过在被测应用中植入一个SDK(或利用系统提供的辅助功能),直接读取应用的UI控件树。它能“理解”屏幕上的内容,知道哪个是按钮、哪个是文本框,以及它们的名称、文本、位置等属性。
- 优点: 精准、稳定、快速。直接通过控件ID或名称定位,不受UI样式变化影响。可以获取和断言控件的内部属性,测试更有深度。
- 缺点: 具有侵入性,需要被测应用配合集成SDK(特别是Unity),或者依赖目标框架的支持。
结论:最佳策略 = Poco 优先,图像保底
- 对于 Unity: 必须集成 Poco-SDK,发挥 Poco 的最大威力。
- 对于 Flutter: Poco 对 Flutter 的支持正在发展中,目前主要依赖系统辅助功能,可能不稳定或信息不全。因此,策略是尝试用Poco,不行就立即用图像识别。
二、 环境准备
- 安装 Airtest IDE: 提供一站式的脚本编写、调试和运行环境,对新手最友好。
- Python 环境: 如果你希望用代码而非IDE来管理项目,请安装 Python,并
pip install airtest
和pip install pocoui
。 - 被测应用 (APK/IPA):
-
- Unity 应用: 【关键步骤】 请开发团队提供一个集成了
Poco-SDK
的测试专用包。集成过程很简单,只需将Poco-SDK的Unity包导入项目即可。这是发挥Poco威力的前提。 - Flutter 应用: 普通的
debug
或profile
包即可。Poco会尝试通过Android的Accessibility
服务来获取控件信息。
- Unity 应用: 【关键步骤】 请开发团队提供一个集成了
- 连接设备:
-
- Android: 准备一台安卓真机或模拟器,开启“开发者选项”和“USB调试”。连接电脑,确保
adb devices
能看到你的设备。 - iOS: 需要一台Mac电脑、Xcode以及WebDriverAgent项目,配置相对复杂。我们先以更通用的Android为例。
- Android: 准备一台安卓真机或模拟器,开启“开发者选项”和“USB调试”。连接电脑,确保
三、 针对 Unity 的自动化测试实战
假设场景: 测试一个Unity游戏的主界面,需要点击“开始游戏”按钮,并验证玩家名字是否正确。
1. 初始化与连接
from airtest.core.api import *
from poco.drivers.unity3d import UnityPoco# 连接设备
auto_setup(__file__, logdir=True, devices=["Android:///"])# 初始化 UnityPoco
# 【关键】Poco会在这里尝试与集成在游戏内的SDK建立通信
poco = UnityPoco()
2. 使用 Poco 精准操作和断言
# 1. 通过控件名称定位并点击“开始游戏”按钮
# 假设开发在Unity中将该按钮命名为 "Btn_Start"
start_button = poco("Btn_Start")
start_button.click()# 2. 等待新场景加载完成,可以等待某个新场景的元素出现
# 假设新场景有个 "Panel_PlayerInfo"
poco("Panel_PlayerInfo").wait_for_appearance()# 3. 获取并断言玩家名字
# 假设显示玩家名字的Text控件被命名为 "Text_PlayerName"
player_name_label = poco("Text_PlayerName")
actual_name = player_name_label.get_text()# 使用Poco断言属性
assert actual_name == "Hero_Player_01", f"玩家名字错误!实际为: {actual_name}"# 也可以用Airtest的断言
# assert_equal(actual_name, "Hero_Player_01", "玩家名字错误!")print("Unity 测试通过!")
3. 当 Poco 失效时,无缝切换到图像识别
假设有一个特殊的粒子效果按钮,它没有控件名。
# 假设Poco无法定位这个特效按钮
# 我们回退到Airtest的图像识别
# "fx_button.png" 是你预先截好的按钮图片
touch(Template(r"fx_button.png", record_pos=(0.0, 0.0), resolution=(1920, 1080)))
Unity 总结:
- 核心是
UnityPoco()
。 - 首选
poco("控件名")
进行定位和操作。 - 利用
.get_text()
、.get_position()
等方法 获取属性,进行深度断言。 - 对于动画、粒子等动态元素,可与开发约定,通过Poco调用一个
freeze_ui()
的接口来暂停动态效果,保证测试稳定。
四、 针对 Flutter 的自动化测试实战
假设场景: 测试一个Flutter应用,需要在一个列表中滑动,找到指定项并点击,然后验证详情页的标题。
1. 初始化与连接
from airtest.core.api import *
from poco.drivers.android.uiautomation import AndroidUiautomationPoco# 连接设备
auto_setup(__file__, logdir=True, devices=["Android:///"])# 初始化 AndroidUiautomationPoco
# 【关键】它通过Android的辅助功能服务来获取UI信息
poco = AndroidUiautomationPoco(use_airtest_input=True, screenshot_each_action=False)
2. 混合策略:Poco + 图像识别
# 1. 尝试用 Poco 进行滑动操作
# Poco的滑动通常比Airtest的更稳定
list_view = poco("android.view.View") # 假设列表是一个通用的View
list_view.swipe("up")# 2. 寻找列表项 "目标Item"
# 方案A: 尝试用Poco通过文本定位(如果辅助功能支持)
try:target_item = poco(text="目标Item")target_item.click()
except Exception:# 方案B: 如果Poco找不到,立即切换到图像识别print("Poco 未找到文本,切换到图像识别")touch(Template(r"target_item.png"))# 3. 验证详情页标题
# 详情页标题通常比较稳定,可以优先尝试图像识别断言
# 这比Poco查找可能更快、更直接
assert_exists(Template(r"detail_page_title.png"), "详情页标题未出现!")# 也可以再次尝试用Poco
# title_element = poco(text="商品详情")
# assert title_element.exists(), "详情页标题未出现!"print("Flutter 测试通过!")
Flutter 总结:
- 核心是
AndroidUiautomationPoco()
。 - 策略上更加灵活,是 Poco 和 Airtest 图像识别的紧密结合。
- 对于列表滑动等通用操作,Poco 更可靠。
- 对于特定元素的查找和断言,两种方法都试试,哪个稳定用哪个。通常静态的、特征明显的UI元素,用图像识别断言 (
assert_exists
) 非常高效。 - 与开发沟通,为关键控件设置好
semanticsLabel
,这对Poco通过辅助功能获取信息非常有帮助。
五、 最佳实践与进阶技巧
- 脚本与图片分离: 不要在代码中写死图片路径。使用
auto_setup(__file__)
后,Airtest 会自动在脚本同级目录寻找图片。建立清晰的图片资产文件夹。 - 多分辨率适配: 这是图像识别的痛点。
-
- 为不同分辨率准备不同图片集,通过脚本动态加载。
- 在
Template()
中设置record_pos
和resolution
,可以提高在不同设备上的识别准确率。 - 尽可能使用Poco,因为它不受分辨率影响。
- 封装通用操作: 将登录、退出等操作封装成函数,在多个脚本中复用。
- 使用断言: 大量使用
assert_exists
、assert_not_exists
、assert_equal
等断言,让脚本真正具备“测试”能力,而不仅仅是“操作”。 - 清晰的报告: Airtest 运行后会生成图文并茂的HTML报告。利用
log()
函数在报告中打印关键信息,便于排查问题。 - 集成CI/CD: 使用
airtest run your_script.air
命令来执行脚本,可以轻松集成到 Jenkins, GitLab CI 等流水线中,实现自动化无人值守测试。