如何使用 Airtest 对 Flutter 和 Unity 应用进行UI自动化测试

使用 Airtest 进行 Flutter/Unity UI 自动化测试终极指南

一、 核心原理:为什么 Airtest 能行?

要理解如何用,先要明白其原理。Airtest 采取了“两条腿走路”的策略,这正是它能通吃各种UI技术的关键。

  1. 第一条腿:基于图像识别 (Airtest Core)
    • 原理: Airtest 将你的手机屏幕看作一张大图片。你提供一个小的目标图片(如一个按钮的截图),Airtest 就在当前屏幕这张大图片中去寻找与目标图片最相似的区域。
    • 优点: “所见即所得”,无视底层技术实现。无论是 Flutter、Unity、原生应用、小程序,甚至是远程桌面,只要它能在屏幕上显示出来,Airtest 就能“看到”并操作它。这是测试 Flutter 和 Unity 的保底方案和通用基础
    • 缺点: 对UI变化敏感(颜色、大小、形状改变会导致识别失败)、性能开销相对较大、无法获取控件内部属性(如文本内容)。
  1. 第二条腿:基于UI控件树 (Poco Framework)
    • 原理: Poco 像一个“翻译官”,它通过在被测应用中植入一个SDK(或利用系统提供的辅助功能),直接读取应用的UI控件树。它能“理解”屏幕上的内容,知道哪个是按钮、哪个是文本框,以及它们的名称、文本、位置等属性。
    • 优点: 精准、稳定、快速。直接通过控件ID或名称定位,不受UI样式变化影响。可以获取和断言控件的内部属性,测试更有深度。
    • 缺点: 具有侵入性,需要被测应用配合集成SDK(特别是Unity),或者依赖目标框架的支持。

结论:最佳策略 = Poco 优先,图像保底

  • 对于 Unity: 必须集成 Poco-SDK,发挥 Poco 的最大威力。
  • 对于 Flutter: Poco 对 Flutter 的支持正在发展中,目前主要依赖系统辅助功能,可能不稳定或信息不全。因此,策略是尝试用Poco,不行就立即用图像识别

二、 环境准备
  1. 安装 Airtest IDE: 提供一站式的脚本编写、调试和运行环境,对新手最友好。
  2. Python 环境: 如果你希望用代码而非IDE来管理项目,请安装 Python,并 pip install airtestpip install pocoui
  3. 被测应用 (APK/IPA):
    • Unity 应用: 【关键步骤】 请开发团队提供一个集成了 Poco-SDK 的测试专用包。集成过程很简单,只需将Poco-SDK的Unity包导入项目即可。这是发挥Poco威力的前提。
    • Flutter 应用: 普通的 debugprofile 包即可。Poco会尝试通过Android的Accessibility服务来获取控件信息。
  1. 连接设备:
    • Android: 准备一台安卓真机或模拟器,开启“开发者选项”和“USB调试”。连接电脑,确保 adb devices 能看到你的设备。
    • iOS: 需要一台Mac电脑、Xcode以及WebDriverAgent项目,配置相对复杂。我们先以更通用的Android为例。

三、 针对 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通过辅助功能获取信息非常有帮助。

五、 最佳实践与进阶技巧
  1. 脚本与图片分离: 不要在代码中写死图片路径。使用 auto_setup(__file__) 后,Airtest 会自动在脚本同级目录寻找图片。建立清晰的图片资产文件夹。
  2. 多分辨率适配: 这是图像识别的痛点。
    • 为不同分辨率准备不同图片集,通过脚本动态加载。
    • Template() 中设置 record_posresolution,可以提高在不同设备上的识别准确率。
    • 尽可能使用Poco,因为它不受分辨率影响。
  1. 封装通用操作: 将登录、退出等操作封装成函数,在多个脚本中复用。
  2. 使用断言: 大量使用 assert_existsassert_not_existsassert_equal 等断言,让脚本真正具备“测试”能力,而不仅仅是“操作”。
  3. 清晰的报告: Airtest 运行后会生成图文并茂的HTML报告。利用 log() 函数在报告中打印关键信息,便于排查问题。
  4. 集成CI/CD: 使用 airtest run your_script.air 命令来执行脚本,可以轻松集成到 Jenkins, GitLab CI 等流水线中,实现自动化无人值守测试。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/pingmian/85345.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/85345.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

河马剧场多部自制剧霸榜,短剧精品化战略持续推进

临近暑期,短剧市场热度逐渐攀升。近期,DataEye发布6月9日—6月15日微短剧热力榜显示河马剧场以8部作品占据TOP30近三分之一席位,前三名中更是占据2个席位,以题材多元化、用户覆盖广、数据爆发力强的特点引领行业风向。其中&#x…

幂级数 (0,R); R ;(R,+oo)

很好,我们现在来回答你问的这个问题: 🟠 幂级数在收敛半径 R R R 以外会发生什么? 我们考虑一个一般形式的幂级数: ∑ n 0 ∞ a n ( x − x 0 ) n \sum_{n0}^{\infty} a_n (x - x_0)^n n0∑∞​an​(x−x0​)n 它的…

C#学习13——正则表达式

一、正则表达式 是一种匹配输入文本的模式,是由特殊字符组成,用于匹配字符串中的字符组合。 二、正则表达式有哪些? 1.Regex 类(引入System.Text.RegularExpressions;) Regex 类用于表示一个正则表达式。 1&#…

【Redis】持久化机制:RDB / AOF 的应用与场景

文章目录 Redis 持久化一、RDB1.1 说明1.2 触发机制手动触发自动触发 1.3 流程说明1.4 文件处理1.5 优缺点 & 适用场景 二、AOF2.1 说明2.2 使用 AOF2.3 命令写入2.4 文件同步2.5 重写机制2.6 启动时数据恢复2.7 优缺点 & 适用场景 三、不使用 AOF / RDB 的情况3.1 场景…

Python 企业级开发与DevOps实践

https://www.python.org/static/community_logos/python-logo-master-v3-TM.png 大型项目结构与设计模式 项目结构规范 text 复制 下载 enterprise_app/ ├── docs/ # 项目文档 ├── tests/ # 测试代码 │ ├── unit/ …

E结构体基础.go

前言:结构体是一种用户自定义的数据类型,它可以将多个不同类型的数据整合在一起,形成一个有机的整体。这就好比在现实生活中,我们有各种各样的物品,它们各自有不同的属性和用途,而结构体就像是一个收纳箱&a…

Spring @Autowired 依赖注入全解析

Autowired 是 Spring 框架中实现依赖注入的核心注解,其自动装配过程可分为以下步骤,结合了类型匹配、名称解析和容器协作机制: 1. 组件扫描与 Bean 定义注册 扫描阶段:Spring 容器启动时,通过 ComponentScan 或 XML 配…

将git的普通目录用idea初始化为maven项目

在 IntelliJ IDEA 中将一个已存在的 Git 目录初始化为 Maven 项目,可以通过以下步骤完成。这些步骤假设你已经有一个包含代码的 Git 仓库,并希望将其转换为 Maven 项目结构,以便更好地管理依赖和构建。 步骤 1:打开或导入 Git 仓库…

Vue 中 key 属性的深入解析:改变 key 导致组件销毁与重建

一、key 属性的核心作用 在 Vue 中,key是一个特殊的属性,主要用于协助 Vue 的虚拟 DOM(Virtual DOM)算法高效地更新实际 DOM。它的核心作用可以概括为: 唯一标识节点:为每个节点提供一个唯一的身份标识优化 Diff 算法:帮助 Vue 准确判断两个节点是否为同一节点(如for循…

【音视频】PJSIP库——示例简介、C++类说明

1、简介 pjsip库的源码中有很多示例,是入门pjsip的第一手资料,下面将各个示例所演示的功能列举出来,以便下一步学习; 最后总结下C++接口主要类及成员函数说明。 2、示例介绍 2.1 音视频处理 aectest 音频回声消除测试工具,用于演示音频处理模块中的回声消除(AEC)功能…

网站用CDN可以防DDoS和CC攻击吗?

现在市面上常见有两种CDN,加速CDN与高防CDN,这两种的区别还是很大的。 加速CDN: 加速CDN基本上都是共享、无防节点,主要做的是加速,所以价格也会相对较低,大陆地区的CDN都需要备案域名接入使用。 高防CD…

【图片识别改名】批量识别图片中的文字对图片进行改名,识别文字对图片重新命名的操作步骤和注意事项

一、应用场景 快递单号识别与管理:在快递业务中,每天会产生大量的快递面单图片。通过咕嘎OCR批量识别面单上的快递单号等关键信息,并以此对图片进行重命名,方便工作人员快速查询和管理快递包裹的物流信息,提高快递处理…

先理解软件工程,再谈AI辅助研发

摘要: 近期行业内对“AI赋能软件工程”的讨论,大多聚焦于代码生成等局部提效,这是一种危险的短视。本文旨在纠正将“软件开发”等同于“编码”的普遍误解,深入探讨软件工程的系统性本质。我们将论证,若缺乏坚实的工程体…

Android软件适配遥控器需求-案例经验分享

不分大屏产品需要有遥控器功能,这里分享部分实战经验 文章目录 前言一、案例部分效果图二、项目基础架构三、焦点基础知识适配遥控器基础-焦点问题焦点管理明确焦点状态布局实现硬编码实现引入第三方自定义组件实现 焦点顺序作用 初始焦点 requestFocus 按键处理获取…

《HTTP权威指南》 第3章 HTTP报文

报文是如何流动的 HTTP报文是在HTTP程序之间发送的数据块。数据块以一些文本形式的元信息开头。 报文方向有:流入、流出、上游、下游。 流入和流出描述事务处理的方向,流入和流出是基于服务器的描述。 流入:客户端发往服务器的请求报文 流…

Kafka 集群架构与高可用方案设计(二)

Kafka 集群架构与高可用方案的优化策略 合理配置参数 在 Kafka 集群的配置中,参数的合理设置对于系统的高可用性和性能表现起着关键作用。例如,min.insync.replicas参数定义了 ISR(In-Sync Replicas,同步副本)集合中…

47-Oracle ASH报告解读

上一期生成了ASH报告后,就需要解读报告关键信息。ASH的使用可以快速定位瞬时性能问题。生产环境的场景时间紧、任务重,但是必须要结合具体业务分析,同时借助其他工具做报告做趋势分析。 一、ASH 技术原理​ ​1. 核心机制​ ​采样原理​&a…

“本地化思维+模块化体验”:一款轻量数据中心监控系统的真实测评

“本地化思维模块化体验”:一款轻量数据中心监控系统的真实测评 在数据中心运维逐步精细化的今天,一款真正贴合本地用户习惯、设计有温度的系统并不多见。近期体验了一款功能全面、逻辑清晰的监控平台,给人留下了深刻印象。并不是广。今天就从…

词编码模型有哪些

词编码模型有哪些 词编码模型在高维向量空间的关系解析与实例说明 如Word2Vec、BERT、Qwen等 一、高维向量空间的基础概念 词编码模型(如Word2Vec、BERT、Qwen等)的核心是将自然语言符号映射为稠密的高维向量,使语义相近的词汇在向量空间中位置接近。以Qwen模型为例,其…

elementui el-select 获取value和label 以及 对象的方法

获取 el-select 的 value 和 label 值 在 Element UI 的 el-select 组件中,可以通过以下方法获取选项的 value 和 label 值。 1、绑定 v-model 获取 value el-select 通常通过 v-model 绑定 value 值,直接访问绑定的变量即可获取当前选中的 value。…