录制只是入门,真正用好Airtest,必须掌握核心API。本篇详解touch、swipe、text、wait、exists、assert等所有关键操作。


目录

  1. Airtest图像识别原理
  2. 操作类API
  3. 等待与查找类API
  4. 断言类API
  5. 辅助类API
  6. Template详解:图像识别的灵魂
  7. 截图技巧:写好脚本的关键
  8. 实战:编写一个完整的测试脚本
  9. 常见问题与排错
  10. 总结

一、Airtest图像识别原理

1.1 它是怎么"看见"按钮的

你的操作:截一张"登录按钮"的图片,保存为 login_btn.png
Airtest的流程:
  ① 截取当前手机屏幕
  ② 在当前屏幕中搜索 login_btn.png 的位置
  ③ 找到匹配位置 → 计算中心坐标 → 点击
  ④ 找不到 → 报错 ImageNotFoundError

1.2 关键概念:置信度(Threshold)

python

复制

# threshold 是图像匹配的"门槛值",范围0~1
# 默认值:0.7(70%相似即认为匹配)

touch(Template(r"btn.png", threshold=0.7))   # 默认,70%相似
touch(Template(r"btn.png", threshold=0.9))   # 严格,90%相似才匹配
touch(Template(r"btn.png", threshold=0.5))   # 宽松,50%相似就匹配
threshold值效果适用场景
0.9 ~ 0.99极严格,几乎一模一样才匹配图标固定不变
0.7 ~ 0.8默认推荐,稳定可靠大多数场景
0.5 ~ 0.6宽松,允许较大差异动画、特效、多分辨率
< 0.5极宽松,容易误匹配不建议使用

1.3 坐标系

python

复制

# Airtest使用相对坐标(比例坐标),而不是像素坐标
# 屏幕中心是 (0, 0)
# 左上角是 (-1, -1)(或接近)
# 右下角是 (1, 1)(或接近)

# 好处:同一脚本在不同分辨率的手机上都能运行!
touch((0, 0))       # 点击屏幕中心
touch((0.5, 0.5))   # 点击右下区域中心
touch((-0.5, 0))    # 点击左侧中间

二、操作类API

2.1 touch —— 点击

python

复制

from airtest.core.api import *

# 方式一:图像识别点击(最常用)
touch(Template(r"login_btn.png"))

# 方式二:坐标点击
touch((500, 800))           # 像素坐标

# 方式三:图片 + 坐标偏移
touch(Template(r"icon.png", target_pos=5))  
# target_pos: 1=左上 2=上中 3=右上 4=左中 5=中心 6=右中 7=左下 8=下中 9=右下

# 方式四:连续点击
touch(Template(r"btn.png"), times=3)        # 点击3次
touch(Template(r"btn.png"), duration=0.5)   # 按下0.5秒(长按)

# 方式五:双指点击
pinch(in_or_out='in', center=None, percent=0.5)  # 缩小
pinch(in_or_out='out', center=None, percent=0.5) # 放大

2.2 swipe —— 滑动

python

复制

# 从起点滑到终点
swipe((100, 1000), (100, 200))      # 从下往上滑(像素坐标)

# 图像识别起点,滑到指定坐标
swipe(Template(r"item.png"), vector=(0, -0.5))  # 从item位置向上滑

# 常用滑动方向(相对坐标)
swipe((0, 0.7), (0, 0.3))           # 上滑(浏览列表)
swipe((0, 0.3), (0, 0.7))           # 下滑(刷新)
swipe((0.8, 0.5), (0.2, 0.5))       # 左滑(翻页)
swipe((0.2, 0.5), (0.8, 0.5))       # 右滑(返回)

# 控制滑动速度
swipe((0, 0.7), (0, 0.3), duration=0.5)   # 快速滑动
swipe((0, 0.7), (0, 0.3), duration=2.0)   # 慢速滑动

# 连续滑动(分多步)
swipe((0, 0.7), (0, 0.5), duration=0.3)
swipe((0, 0.5), (0, 0.3), duration=0.3)

2.3 text —— 文本输入

python

复制

2.4 keyevent —— 按键事件

python

复制

# 常用按键
keyevent("HOME")        # Home键
keyevent("BACK")        # 返回键
keyevent("MENU")        # 菜单键
keyevent("VOLUME_UP")   # 音量+
keyevent("VOLUME_DOWN") # 音量-
keyevent("POWER")       # 电源键
keyevent("ENTER")       # 回车键
keyevent("DELETE")      # 删除键
keyevent("SEARCH")      # 搜索键

# 组合键
keyevent("HOME")        # 回到桌面
sleep(0.5)
keyevent("RECENTAPPS")  # 最近任务

2.5 snapshot —— 截图

python

复制

# 截取当前屏幕
snapshot(filename="当前屏幕.png")

# 截取指定区域
snapshot(filename="局部.png", rect=(100, 200, 300, 400))
# rect: (左上x, 左上y, 右下x, 右下y)

# 在报告中显示截图(调试用)
snapshot(msg="操作前")
touch(Template(r"btn.png"))
snapshot(msg="操作后")

三、等待与查找类API

3.1 sleep —— 固定等待

python

复制

sleep(1)        # 等待1秒
sleep(0.5)      # 等待0.5秒
sleep(3)        # 等待3秒

# 使用场景:页面加载需要时间、动画播放需要时间
touch(Template(r"login_btn.png"))
sleep(2)        # 等待登录后的页面加载
assert_exists(Template(r"home_icon.png"), "登录成功")

注意:sleep是固定等待,不够智能。网络慢时可能不够,网络快时浪费。尽量用wait代替。

3.2 wait —— 智能等待(推荐!)

python

复制

# 等待某个元素出现,默认超时20秒
wait(Template(r"loading_complete.png"))

# 指定超时时间
wait(Template(r"result.png"), timeout=30)     # 最多等30秒
wait(Template(r"popup.png"), timeout=5)       # 最多等5秒

# 等待 + 超时后执行操作
wait(Template(r"btn.png"), timeout=10, interval=0.5)
# timeout: 最长等待时间(秒)
# interval: 检测间隔(秒),每隔0.5秒检测一次

# wait 的工作原理:
# 每隔 interval 秒截一次屏 → 搜索目标图片 → 找到就返回 → 超时就报错

3.3 exists —— 判断是否存在

python

复制

# 判断元素是否存在,返回 True 或 False
if exists(Template(r"ad_popup.png")):
    touch(Template(r"close_ad.png"))
    print("关闭了广告弹窗")
else:
    print("没有广告弹窗,直接继续")

# exists不会报错,找不到返回False
# 默认检测时间约3秒

3.4 wait vs exists vs sleep 对比

API行为找不到时适用场景
sleep(N)固定等待N秒不检查已知固定耗时
wait(img)循环查找直到出现抛异常必须出现的元素
exists(img)查找一次返回结果返回False可能不出现的元素

python

复制

# 最佳实践:组合使用

# 场景:登录后等待首页加载(必须出现)
touch(Template(r"login_btn.png"))
wait(Template(r"home_tab.png"), timeout=10)    # 必须等到首页出现

# 场景:可能弹出更新提示(可能不出现)
if exists(Template(r"update_dialog.png")):
    touch(Template(r"cancel_btn.png"))          # 有弹窗就关掉

# 场景:固定动画时间
touch(Template(r"start_btn.png"))
sleep(2)    # 等开场动画播放(动画时长固定,用sleep更合适)

3.5 循环等待(进阶技巧)

python

复制

# 场景:等待列表加载完成(不确定要等多久)
for i in range(10):
    if exists(Template(r"list_item.png")):
        break
    sleep(1)
else:
    raise Exception("列表10秒内未加载完成")

# 场景:等待加载动画消失
# 加载中的转圈消失 → 页面加载完成
for i in range(30):  # 最多等30秒
    if not exists(Template(r"loading_spinner.png")):
        print("加载完成")
        break
    sleep(1)

四、断言类API

4.1 assert_exists —— 验证存在

python

复制

# 验证元素存在(最常用)
assert_exists(Template(r"success_msg.png"), "应该显示成功提示")

# 如果找不到,报告显示:断言失败,应该显示成功提示

4.2 assert_not_exists —— 验证不存在

python

复制

# 验证元素不存在
assert_not_exists(Template(r"error_msg.png"), "不应该显示错误提示")

# 验证广告已关闭
touch(Template(r"close_ad.png"))
sleep(1)
assert_not_exists(Template(r"ad_banner.png"), "广告应该已关闭")

4.3 assert_equal —— 验证相等

python

复制

# 比较两个值是否相等
actual = poco("result_text").get_text()
assert_equal(actual, "3", "计算结果应为3")

# 比较数字
assert_equal(len(poco("list_item")), 10, "列表应有10条数据")

4.4 断言实战组合

python

复制

# 完整的验证流程

# 1. 操作前验证初始状态
assert_exists(Template(r"login_page.png"), "应在登录页面")

# 2. 执行操作
touch(Template(r"username_input.png"))
text("admin")
touch(Template(r"password_input.png"))
text("123456")
touch(Template(r"login_btn.png"))

# 3. 等待结果
wait(Template(r"home_page.png"), timeout=10)

# 4. 操作后验证
assert_exists(Template(r"welcome_text.png"), "登录成功应显示欢迎语")
assert_not_exists(Template(r"login_page.png"), "不应停留在登录页")

五、辅助类API

5.1 start_app / stop_app

python

复制

# 启动APP
start_app("com.example.app")            # Android包名
start_app("com.tencent.mm")             # 启动微信

# 停止APP
stop_app("com.example.app")

# 重启APP(测试常用)
stop_app("com.example.app")
sleep(1)
start_app("com.example.app")

5.2 clear_app

python

复制

# 清除APP数据(恢复到初始状态)
clear_app("com.example.app")
# 等同于:设置 → 应用 → 清除数据

# 测试场景:每个用例执行前清除数据,保证独立性

5.3 install / uninstall

python

复制

# 安装APK
install(r"C:\app\release.apk")

# 卸载APP
uninstall("com.example.app")

5.4 wake / home

python

复制

# 唤醒屏幕
wake()

# 回到桌面
home()

# 组合:确保手机处于可用状态
def ensure_device_ready():
    wake()              # 唤醒屏幕
    sleep(0.5)
    home()              # 回到桌面
    sleep(0.5)
    stop_app("com.example.app")
    sleep(0.5)
    start_app("com.example.app")
    sleep(2)

六、Template详解:图像识别的灵魂

6.1 Template的完整参数

python

复制

Template(
    filename=r"btn.png",        # 图片文件名(必填)
    threshold=0.7,              # 置信度阈值(0~1,默认0.7)
    target_pos=5,               # 点击位置:1-9(九宫格),默认5(中心)
    record_pos=(0, 0),          # 录制时的相对坐标
    resolution=(1080, 1920),    # 录制时的屏幕分辨率
    rgb=False                   # 是否开启彩色识别(默认灰度)
)

6.2 threshold 调优实战

python

复制

# 场景一:按钮在不同页面有轻微色差
# 默认0.7找不到 → 降低到0.6
touch(Template(r"btn.png", threshold=0.6))

# 场景二:多个相似图标容易误匹配
# 默认0.7误点 → 提高到0.85
touch(Template(r"specific_icon.png", threshold=0.85))

# 场景三:游戏中技能图标有发光特效
# 图像变化大 → 降低到0.5
touch(Template(r"skill_icon.png", threshold=0.5))

6.3 target_pos 九宫格定位

target_pos 参数:点击截图的哪个位置

  1(左上)   2(上中)   3(右上)
  4(左中)   5(中心)   6(右中)
  7(左下)   8(下中)   9(右下)

示例:
touch(Template(r"card.png", target_pos=8))
# 点击卡片的底部中间(通常那里是按钮)

touch(Template(r"long_btn.png", target_pos=3))
# 点击长按钮的右上角

6.4 rgb彩色识别

python

复制

# 默认是灰度识别(把彩色图转灰度再匹配)
# 适合大多数场景

# 开启彩色识别(颜色是关键区分特征时)
touch(Template(r"red_btn.png", rgb=True))
# 适用于:彩色图标、状态指示灯、红绿按钮区分

# 注意:rgb=True 识别速度更慢,只在必要时使用

6.5 多分辨率适配

python

复制

# 方法一:使用record_pos和resolution(推荐)
# IDE录制时自动记录坐标和分辨率
touch(Template(r"btn.png", record_pos=(0.2, 0.5), resolution=(1080, 1920)))
# 在不同分辨率手机上,Airtest会自动换算坐标

# 方法二:使用相对坐标直接点击
touch((0.2, 0.5))  # 屏幕宽度的20%,高度的50%

七、截图技巧:写好脚本的关键

7.1 截图的黄金法则

✅ 好的截图:
  1. 截取特征明显的区域(有文字、有独特图标)
  2. 尽量小(只截按钮本身,不要背景)
  3. 避开动态内容(时间、电量、信号)
  4. 在目标设备上截(不要在模拟器上截图给真机用)
  5. 统一分辨率(所有截图在同一设备上截)

❌ 坏的截图:
  1. 截了整屏 → 太慢,容易受其他元素干扰
  2. 包含时间/电量 → 每次都不一样,永远匹配不上
  3. 在1080p手机上截,在720p手机上跑 → 可能不匹配
  4. 截了半透明元素 → 背景不同导致匹配失败
  5. 包含动画帧 → 每次截都不一样

7.2 截图大小与识别速度

python

复制

# 截图大小直接影响识别速度
# 整屏截图(1080×1920):~200ms
# 小图标截图(50×50):~10ms

# 优化原则:截得越小越好,但必须包含足够的特征

7.3 动态内容的处理

python

复制

# 问题:时间、电量、信号等会变化
# 解决:裁剪掉动态部分,只保留静态部分

# 问题:列表内容会滚动变化
# 解决:截取列表标题栏(固定不变),不截列表项

# 问题:按钮文字会变化(如"剩余3次")
# 解决:只截按钮背景/边框,不截文字部分

7.4 批量截图管理

# 建议的截图命名规范
login_btn.png          # 登录按钮
login_btn_disabled.png # 登录按钮(禁用态)
home_tab.png           # 首页Tab
home_tab_active.png    # 首页Tab(选中态)
search_input.png       # 搜索输入框
search_result_item.png # 搜索结果项
popup_close.png        # 弹窗关闭按钮
error_toast.png        # 错误提示Toast

八、实战:编写一个完整的测试脚本

8.1 需求

测试一个天气APP的基本功能:

  1. 启动APP,等待首页加载
  2. 点击搜索框,输入"北京"
  3. 验证搜索结果包含"北京"
  4. 点击"北京",查看详情
  5. 验证详情页显示温度和天气描述

8.2 完整脚本

python

复制

8.3 脚本优化要点

python

复制


九、常见问题与排错

9.1 图像识别失败

python

复制

# 问题1:TargetNotFoundError: 找不到图片
# 可能原因及解决:

# ① 截图和设备当前画面不匹配 → 重新截图
# ② 页面还没加载完就找了 → 加 wait 或 sleep
# ③ threshold太高 → 降低到0.6
# ④ 截图中包含动态内容 → 重新截静态部分
# ⑤ 分辨率不一致 → 在同设备上截图

# 调试技巧:打印当前屏幕
snapshot(filename="debug_current.png")
# 对比你的截图和当前屏幕,看哪里不同

9.2 点击位置不准

python

复制

# 问题:点击位置偏移
# 原因:截图范围太大,Airtest计算的中心点不在按钮上

# 解决1:缩小截图范围,只截按钮本身
# 解决2:使用 target_pos 微调点击位置
touch(Template(r"big_area.png", target_pos=8))  # 点下半部分

# 解决3:先定位再偏移点击
pos = wait(Template(r"icon.png"))
touch((pos[0], pos[1] + 50))  # 在图标下方50像素处点击

9.3 滑动不生效

python

复制

# 问题:swipe没反应
# 可能原因:起点和终点太近、duration太短

# 解决:增大滑动距离,增加duration
swipe((0, 0.8), (0, 0.2), duration=1.0)  # 大幅滑动,持续1秒

# 连续滑动(对付惯性滚动)
for _ in range(3):
    swipe((0, 0.7), (0, 0.3), duration=0.5)
    sleep(0.5)

9.4 文本输入失败

python

复制

# 中文输入失败
# 解决:使用剪贴板方式
import pyperclip
pyperclip.copy("中文测试")
keyevent("PASTE")  # 粘贴(部分设备支持)

# 或者使用ADB方式
text("test")  # 英文一般没问题

9.5 脚本在不同设备上表现不一致

python

复制

# 原因:不同手机分辨率、DPI、字体不同

# 解决策略:
# ① 使用相对坐标而非像素坐标
# ② 同一脚本在不同设备上各准备一套截图
# ③ 使用Poco控件识别代替图像识别(更稳定)
# ④ 降低threshold增加容错

十、总结

API速查表

分类API说明
点击touch(img)图像识别点击
touch((x,y))坐标点击
滑动swipe(p1, p2)从p1滑到p2
swipe(p1, v)从p1按向量v滑动
输入text("内容")输入文本
keyevent("HOME")按键事件
等待sleep(N)固定等待N秒
wait(img, timeout=N)智能等待(推荐)
exists(img)判断是否存在
断言assert_exists(img, msg)验证存在
assert_not_exists(img, msg)验证不存在
应用start_app(pkg)启动APP
stop_app(pkg)停止APP
clear_app(pkg)清除APP数据
截图snapshot()截图保存到报告

图像识别核心口诀

截图要小不要大,特征明显好匹配。
阈值默认零点七,找不到就降一降。
动态内容要避开,时间电量不能截。
多设备跑用坐标,图像识别作备份。
wait比sleep更智能,exists判断不报错。
断言写在操作后,报告清晰好排查。

下一篇预告:Airtest自动化测试专题第三篇——「Poco控件识别:精准定位UI元素」,掌握Poco的控件树定位、层级选择、等待与断言,以及Airtest+Poco混合编程。


本文约5500字,是「Airtest自动化测试专题」第2篇。建议打开AirtestIDE,跟着示例敲一遍,印象最深。

更多推荐