Airtest 框架详解:游戏/Android App 图像+UI树双引擎自动化

做自动化测试时,你大概率会遇到这些头疼场景:Unity/Cocos 游戏没有公开控件 ID原生元素抓不到的小众 App混合开发的“缝合怪”界面。这时候,网易开源的 Airtest 双引擎框架就是救命稻草——它既能靠图像识别“截图即脚本”,又能用 Poco 解析 UI 树精准操控,无需啃完几百页 UI 文档。

本篇文章从零开始,带你搭好开发环境、封装通用基类,再手写一个抖音短视频自动交互脚本(点赞、评论、滑视频),最后还会分享风控规避和图像识别优化技巧。


一、Airtest 双引擎是什么

Airtest Project 主要由两部分组成:

  • Airtest:基于图像识别的自动化框架,通过截图匹配来定位元素,不依赖系统控件树,特别适合游戏引擎(Unity、Cocos2d-x、Egret)和无法获取 UI 树的场景。
  • Poco:基于 UI 控件的自动化框架,直接从安卓/iOS 原生或游戏引擎的 UI 树中获取控件属性(文本、位置、层级等),执行点击、滑动、输入等操作,稳定性远高于图像识别。

两者可以在同一脚本里无缝混合使用,真正实现“哪边方便用哪边”。比如在抖音这种混合 App 里,整体界面用图像识别处理,评论区列表控件用 Poco 精准操作,效率极高。


二、environment-setup——5 分钟跑通第一个脚本

2.1 安装 AirtestIDE(推荐新手)

Airtest 提供了图形化的 IDE,内置录制回放、设备连接、报告查看等功能,适合快速上手:

  1. 前往 Airtest 官网 下载对应系统的 IDE 安装包。
  2. 安装后打开 AirtestIDE,默认自带 airtestpocoui 两个核心库。
  3. 安卓手机开启 开发者模式USB 调试,用数据线连接电脑,IDE 设备面板点击 Connect 即可看到手机画面。

2.2 使用 pip 构建脚本工程(适合集成 CI/CD)

如果要将 Airtest 集成到已有自动化项目或 CI 流水线,推荐用纯 Python 脚本方式:

pip install airtest
pip install pocoui

验证安装:

from airtest.core.api import *
from poco.drivers.android.uiautomation import AndroidUiautomationPoco

# 连接设备(USB 连接串号可通过 adb devices 查看)
connect_device("Android:///")
poco = AndroidUiautomationPoco()
print(poco.device)

2.3 必要工具链

  • ADB:安卓调试桥,Airtest 底层依赖它操控设备。配置好环境变量,确保终端执行 adb devices 能够列出设备。
  • 手机设置:除 USB 调试外,建议开启“指针位置”方便查看坐标,关闭系统自动旋转、省电模式等干扰项。

三、基础操作速览——Airtest 图像引擎怎么用

图像识别的核心思想:先截取目标元素的“模板图”,脚本运行时在设备截屏中查找该图,找到后执行操作。

3.1 常用 API

from airtest.core.api import *

# 连接设备
connect_device("Android:///")

# 点击(模板图需提前截图放在项目目录)
touch(Template("like_btn.png"))

# 滑动
swipe((500, 1500), (500, 500))   # 从下往上滑

# 文本输入
text("很棒的视频")

# 按键事件
keyevent("HOME")

# 等待元素出现
wait(Template("comment_input.png"), timeout=10)

# 断言是否存在
assert_exists(Template("success_tip.png"), "点赞成功")

3.2 图像模板制作技巧

  • 截取小范围且特征明显的区域(比如点赞按钮只有心形图标部分,不要带背景色块)。
  • 保存为 PNG 格式,放在项目的 images/ 目录下,便于管理。
  • 如果识别不稳定,可以用 touch(Template("btn.png", threshold=0.7)) 降低置信度阈值。

四、Poco 引擎——抓取 UI 树精准操控

Poco 不需要截图,直接通过反射或 AccessibilityService 获取控件树。

4.1 初始化 Poco

from poco.drivers.android.uiautomation import AndroidUiautomationPoco

poco = AndroidUiautomationPoco(use_airtest_input=True, screenshot_each_action=False)

参数说明:

  • use_airtest_input=True:通过 Airtest 设备连接执行点击,避免多重输入源冲突。
  • screenshot_each_action=False:不在每次操作后截图,提升速度。

4.2 常用操作示例

# 通过文本查找
poco(text="首页").click()

# 通过 ID 或层级关系
poco("android.widget.ListView").child("android.widget.TextView")[0].click()

# 滑动列表
poco("com.ss.android.ugc.aweme:id/recycler_view").swipe([0, -0.3])

# 获取属性
name = poco(text="视频标题").attr("text")

4.3 混合使用:Airtest + Poco

# 先用图像识别点击“评论”按钮
touch(Template("comment_icon.png"))

# 再通过 Poco 定位评论输入框输入文字
poco("android.widget.EditText").set_text("加油!")

五、实战:编写抖音短视频自动交互脚本

下面我们为抖音(TikTok 国内版)写一个自动化脚本,模拟日常浏览操作:刷视频、点赞、评论一条龙。

注意:本教程仅用于学习自动化测试技术,请勿用于刷量、虚假互动等违反平台规定的行为。

5.1 场景设计

  1. 打开抖音 App,等待首页加载。
  2. 连续滑动 3 个视频,每个视频停留 2 秒以上。
  3. 对第 4 个视频点赞并打开评论框。
  4. 随机输入一条正面评论并发送。
  5. 退出评论,继续滑动。

5.2 目录结构

douyin_auto/
├── main.py              # 主脚本
├── base_operator.py     # 封装基类
├── images/              # 图像模板
│   ├── home_tab.png     # 首页 Tab
│   ├── like_btn.png     # 点赞红心
│   ├── comment_btn.png  # 评论图标
│   └── close_comment.png# 关闭评论
└── requirements.txt

5.3 封装通用基类 base_operator.py

import random
import time
from airtest.core.api import *
from poco.drivers.android.uiautomation import AndroidUiautomationPoco

class BaseOperator:
    def __init__(self, device_uri="Android:///"):
        self.device = connect_device(device_uri)
        self.poco = AndroidUiautomationPoco(
            use_airtest_input=True,
            screenshot_each_action=False
        )
        self.package = "com.ss.android.ugc.aweme"
    
    def start_app(self):
        """启动抖音"""
        stop_app(self.package)
        time.sleep(1)
        start_app(self.package)
        sleep(5)  # 等待首页加载
    
    def swipe_video(self, direction="up", duration=0.3):
        """滑动视频,up 向上滑看下一个,down 向下滑看上一个"""
        if direction == "up":
            swipe((540, 1600), (540, 400), duration=duration)
        else:
            swipe((540, 400), (540, 1600), duration=duration)
        sleep(2)  # 停留观看时间
    
    def like_current_video(self):
        """点赞当前视频(双引擎保底)"""
        try:
            # 优先用 Poco 查找点赞按钮
            like_btn = self.poco(name="com.ss.android.ugc.aweme:id/ae8")
            if like_btn.exists():
                like_btn.click()
                return
        except Exception:
            pass
        # 降级为图像识别
        touch(Template("images/like_btn.png", threshold=0.7))
    
    def comment_current_video(self, text_content):
        """评论当前视频"""
        # 点击评论图标
        try:
            self.poco(desc="评论").click()
        except Exception:
            touch(Template("images/comment_btn.png"))
        
        sleep(1)
        # 输入评论内容
        self.poco("android.widget.EditText").set_text(text_content)
        sleep(0.5)
        # 发送按钮(不同版本可能是“发送”文本或发送图标)
        send_btn = self.poco(text="发送")
        if send_btn.exists():
            send_btn.click()
        else:
            self.poco(type="android.widget.ImageView", desc="发送").click()
        sleep(1)
        # 关闭评论面板
        self.close_comment_panel()
    
    def close_comment_panel(self):
        """关闭评论面板,回退到短视频界面"""
        keyevent("BACK")
        sleep(0.5)
        # 如果 BACK 键无效,用图像点击关闭按钮
        if exists(Template("images/close_comment.png")):
            touch(Template("images/close_comment.png"))
    
    def random_comment(self):
        """生成随机正面评论"""
        comments = ["太厉害了👍", "学到了", "支持创作者", "已点赞", "这内容绝了",
                    "幽默风趣哈哈", "每天必看", "这个系列能多出吗"]
        return random.choice(comments)
    
    def run_interaction_flow(self, swipe_count=3):
        """完整交互流程"""
        self.start_app()
        # 前 swipe_count 个视频只滑动
        for i in range(swipe_count):
            self.swipe_video("up")
        # 第 swipe_count+1 个视频执行点赞+评论
        sleep(1)
        self.like_current_video()
        sleep(0.5)
        self.comment_current_video(self.random_comment())
        # 继续滑动几个视频
        for i in range(2):
            self.swipe_video("up")

5.4 主入口 main.py

from base_operator import BaseOperator

if __name__ == "__main__":
    operator = BaseOperator()
    try:
        operator.run_interaction_flow(swipe_count=3)
        print("自动化流程完成")
    except Exception as e:
        print(f"脚本运行出错: {e}")

5.5 图像模板准备

实际操作中,用 AirtestIDE 的“截图”功能获取设备当前画面,裁剪出以下图片放到 images/

  • like_btn.png:点赞按钮(未点赞状态的红心框)
  • comment_btn.png:评论图标(气泡形状)
  • close_comment.png:评论页面的关闭/返回按钮

尽量保持设备分辨率和截图时一致,以提高识别率。


六、风控规避——别让平台把你当成机器人

自动化脚本如果行为过于机械,很容易触发平台的风控机制(限制互动、要求验证码甚至封号)。以下是一些防御性策略:

6.1 随机化操作

# 滑动距离加入随机偏移
import random
x1, y1 = 540, 1600
x2, y2 = 540 + random.randint(-50, 50), 400 + random.randint(-30, 30)
swipe((x1, y1), (x2, y2), duration=random.uniform(0.2, 0.5))

# 每条评论间隔随机等待
sleep(random.uniform(3, 8))

6.2 模拟真实用户行为

  • 不要只点赞不浏览:让脚本有“观看时长”概念,先在视频上停留随机时间再操作。
  • 偶尔“误触”回滑:真实用户有时会划回上一个视频,可设置一定概率执行 swipe("down")
  • 交互频率限制:控制每日操作总量,分时段执行,避开连续高频操作。

6.3 账号环境伪装

  • 使用真实常用的设备 ID 和 IP 环境,避免同一 IP 大量操作多个账号。
  • 尽量保留设备原始的传感器数据、GPS 等信息(避免模拟器特征过重),可考虑使用真机集群

七、图像识别优化——从 70% 到 99% 的稳定性飞跃

图像识别是 Airtest 最核心也最容易受环境影响的功能,以下优化手段能大幅提高成功率。

7.1 合适的阈值与灰度处理

# 降低阈值(默认 0.7)以适应轻微变化
touch(Template("btn.png", threshold=0.6, rgb=True))

# 使用灰度模板,忽略色彩差异(适用于按钮背景色渐变的情况)
touch(Template("btn.png", threshold=0.8, rgb=False))

7.2 多分辨率适配

不同手机分辨率下,控件大小和位置都会变化。解决方案:

  • 优先用 Poco 定位,Poco 基于 UI 树不受分辨率影响。
  • 对于图像模板,可录制相对坐标脚本:先找到固定锚点(如底部状态栏图标),然后以此为参考偏移点击。Airtest IDE 支持基于锚点的相对坐标录制。

7.3 动态等待与重试机制

def safe_touch(template, max_retry=3):
    for i in range(max_retry):
        if exists(template):
            touch(template)
            return True
        sleep(1)
    raise Exception(f"未找到元素: {template}")

7.4 模板更新策略

界面改版后会识别失败,可以建立模板仓库并在执行前检查版本。高级用法:编写“自愈”逻辑,当图像匹配失败时自动触发截图回调,通过人工或图像算法更新模板。

try:
    touch(Template("comment.png"))
except TargetNotFoundError:
    snapshot(filename="error_screen.png")
    # 上报到监控平台,通知人工介入

八、进阶技巧与总结

  • 报告生成:Airtest 脚本运行后会在当前目录生成 log/,里面有 HTML 报告和每步截图,方便 Debug。
  • 跨平台:Airtest 同样支持 iOS(需 WebDriverAgent 和 Mac)、Windows 窗口程序的自动化。
  • 嵌入 pytest:可以把 Airtest 操作封装成 pytest 测试用例,纳入持续集成流水线。

双引擎模式下,我们真正做到了“鱼与熊掌兼得”:游戏的封闭图形用图像识别攻克,原生 App 的动态列表用 Poco 精准抓取。配上抖音实战脚本和风控优化,这套方案已经可以直接迁移到直播监控、内容审核自动化、社交运营工具等实际场景中。

现在,打开你的 IDE,连接手机,试着用 touch(Template("xxx.png")) 写出第一行自动化代码吧——不需深究 UI 源码,也不用苦读几百页控件文档,所见即所得的自动化,就从 Airtest 开始。