Airtest自动化测试第二篇:图像识别脚本编写——核心API详解与实战技巧

📅 2026/7/7 18:06:39 👁️ 阅读次数 📝 编程学习
Airtest自动化测试第二篇:图像识别脚本编写——核心API详解与实战技巧

录制只是入门,真正用好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,跟着示例敲一遍,印象最深。