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

发布时间:2026/7/7 17:56:40
Airtest自动化测试第二篇:图像识别脚本编写——核心API详解与实战技巧 录制只是入门真正用好Airtest必须掌握核心API。本篇详解touch、swipe、text、wait、exists、assert等所有关键操作。目录Airtest图像识别原理操作类API等待与查找类API断言类API辅助类APITemplate详解图像识别的灵魂截图技巧写好脚本的关键实战编写一个完整的测试脚本常见问题与排错总结一、Airtest图像识别原理1.1 它是怎么看见按钮的你的操作截一张登录按钮的图片保存为 login_btn.png Airtest的流程 ① 截取当前手机屏幕 ② 在当前屏幕中搜索 login_btn.png 的位置 ③ 找到匹配位置 → 计算中心坐标 → 点击 ④ 找不到 → 报错 ImageNotFoundError1.2 关键概念置信度Thresholdpython复制# threshold 是图像匹配的门槛值范围0~1 # 默认值0.770%相似即认为匹配 touch(Template(rbtn.png, threshold0.7)) # 默认70%相似 touch(Template(rbtn.png, threshold0.9)) # 严格90%相似才匹配 touch(Template(rbtn.png, threshold0.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)) # 点击左侧中间二、操作类API2.1 touch —— 点击python复制from airtest.core.api import * # 方式一图像识别点击最常用 touch(Template(rlogin_btn.png)) # 方式二坐标点击 touch((500, 800)) # 像素坐标 # 方式三图片 坐标偏移 touch(Template(ricon.png, target_pos5)) # target_pos: 1左上 2上中 3右上 4左中 5中心 6右中 7左下 8下中 9右下 # 方式四连续点击 touch(Template(rbtn.png), times3) # 点击3次 touch(Template(rbtn.png), duration0.5) # 按下0.5秒长按 # 方式五双指点击 pinch(in_or_outin, centerNone, percent0.5) # 缩小 pinch(in_or_outout, centerNone, percent0.5) # 放大2.2 swipe —— 滑动python复制# 从起点滑到终点 swipe((100, 1000), (100, 200)) # 从下往上滑像素坐标 # 图像识别起点滑到指定坐标 swipe(Template(ritem.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), duration0.5) # 快速滑动 swipe((0, 0.7), (0, 0.3), duration2.0) # 慢速滑动 # 连续滑动分多步 swipe((0, 0.7), (0, 0.5), duration0.3) swipe((0, 0.5), (0, 0.3), duration0.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(rbtn.png)) snapshot(msg操作后)三、等待与查找类API3.1 sleep —— 固定等待python复制sleep(1) # 等待1秒 sleep(0.5) # 等待0.5秒 sleep(3) # 等待3秒 # 使用场景页面加载需要时间、动画播放需要时间 touch(Template(rlogin_btn.png)) sleep(2) # 等待登录后的页面加载 assert_exists(Template(rhome_icon.png), 登录成功)注意sleep是固定等待不够智能。网络慢时可能不够网络快时浪费。尽量用wait代替。3.2 wait —— 智能等待推荐python复制# 等待某个元素出现默认超时20秒 wait(Template(rloading_complete.png)) # 指定超时时间 wait(Template(rresult.png), timeout30) # 最多等30秒 wait(Template(rpopup.png), timeout5) # 最多等5秒 # 等待 超时后执行操作 wait(Template(rbtn.png), timeout10, interval0.5) # timeout: 最长等待时间秒 # interval: 检测间隔秒每隔0.5秒检测一次 # wait 的工作原理 # 每隔 interval 秒截一次屏 → 搜索目标图片 → 找到就返回 → 超时就报错3.3 exists —— 判断是否存在python复制# 判断元素是否存在返回 True 或 False if exists(Template(rad_popup.png)): touch(Template(rclose_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(rlogin_btn.png)) wait(Template(rhome_tab.png), timeout10) # 必须等到首页出现 # 场景可能弹出更新提示可能不出现 if exists(Template(rupdate_dialog.png)): touch(Template(rcancel_btn.png)) # 有弹窗就关掉 # 场景固定动画时间 touch(Template(rstart_btn.png)) sleep(2) # 等开场动画播放动画时长固定用sleep更合适3.5 循环等待进阶技巧python复制# 场景等待列表加载完成不确定要等多久 for i in range(10): if exists(Template(rlist_item.png)): break sleep(1) else: raise Exception(列表10秒内未加载完成) # 场景等待加载动画消失 # 加载中的转圈消失 → 页面加载完成 for i in range(30): # 最多等30秒 if not exists(Template(rloading_spinner.png)): print(加载完成) break sleep(1)四、断言类API4.1 assert_exists —— 验证存在python复制# 验证元素存在最常用 assert_exists(Template(rsuccess_msg.png), 应该显示成功提示) # 如果找不到报告显示断言失败应该显示成功提示4.2 assert_not_exists —— 验证不存在python复制# 验证元素不存在 assert_not_exists(Template(rerror_msg.png), 不应该显示错误提示) # 验证广告已关闭 touch(Template(rclose_ad.png)) sleep(1) assert_not_exists(Template(rad_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(rlogin_page.png), 应在登录页面) # 2. 执行操作 touch(Template(rusername_input.png)) text(admin) touch(Template(rpassword_input.png)) text(123456) touch(Template(rlogin_btn.png)) # 3. 等待结果 wait(Template(rhome_page.png), timeout10) # 4. 操作后验证 assert_exists(Template(rwelcome_text.png), 登录成功应显示欢迎语) assert_not_exists(Template(rlogin_page.png), 不应停留在登录页)五、辅助类API5.1 start_app / stop_apppython复制# 启动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_apppython复制# 清除APP数据恢复到初始状态 clear_app(com.example.app) # 等同于设置 → 应用 → 清除数据 # 测试场景每个用例执行前清除数据保证独立性5.3 install / uninstallpython复制# 安装APK install(rC:\app\release.apk) # 卸载APP uninstall(com.example.app)5.4 wake / homepython复制# 唤醒屏幕 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( filenamerbtn.png, # 图片文件名必填 threshold0.7, # 置信度阈值0~1默认0.7 target_pos5, # 点击位置1-9九宫格默认5(中心) record_pos(0, 0), # 录制时的相对坐标 resolution(1080, 1920), # 录制时的屏幕分辨率 rgbFalse # 是否开启彩色识别默认灰度 )6.2 threshold 调优实战python复制# 场景一按钮在不同页面有轻微色差 # 默认0.7找不到 → 降低到0.6 touch(Template(rbtn.png, threshold0.6)) # 场景二多个相似图标容易误匹配 # 默认0.7误点 → 提高到0.85 touch(Template(rspecific_icon.png, threshold0.85)) # 场景三游戏中技能图标有发光特效 # 图像变化大 → 降低到0.5 touch(Template(rskill_icon.png, threshold0.5))6.3 target_pos 九宫格定位target_pos 参数点击截图的哪个位置 1(左上) 2(上中) 3(右上) 4(左中) 5(中心) 6(右中) 7(左下) 8(下中) 9(右下) 示例 touch(Template(rcard.png, target_pos8)) # 点击卡片的底部中间通常那里是按钮 touch(Template(rlong_btn.png, target_pos3)) # 点击长按钮的右上角6.4 rgb彩色识别python复制# 默认是灰度识别把彩色图转灰度再匹配 # 适合大多数场景 # 开启彩色识别颜色是关键区分特征时 touch(Template(rred_btn.png, rgbTrue)) # 适用于彩色图标、状态指示灯、红绿按钮区分 # 注意rgbTrue 识别速度更慢只在必要时使用6.5 多分辨率适配python复制# 方法一使用record_pos和resolution推荐 # IDE录制时自动记录坐标和分辨率 touch(Template(rbtn.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的基本功能启动APP等待首页加载点击搜索框输入北京验证搜索结果包含北京点击北京查看详情验证详情页显示温度和天气描述8.2 完整脚本python复制8.3 脚本优化要点python复制九、常见问题与排错9.1 图像识别失败python复制# 问题1TargetNotFoundError: 找不到图片 # 可能原因及解决 # ① 截图和设备当前画面不匹配 → 重新截图 # ② 页面还没加载完就找了 → 加 wait 或 sleep # ③ threshold太高 → 降低到0.6 # ④ 截图中包含动态内容 → 重新截静态部分 # ⑤ 分辨率不一致 → 在同设备上截图 # 调试技巧打印当前屏幕 snapshot(filenamedebug_current.png) # 对比你的截图和当前屏幕看哪里不同9.2 点击位置不准python复制# 问题点击位置偏移 # 原因截图范围太大Airtest计算的中心点不在按钮上 # 解决1缩小截图范围只截按钮本身 # 解决2使用 target_pos 微调点击位置 touch(Template(rbig_area.png, target_pos8)) # 点下半部分 # 解决3先定位再偏移点击 pos wait(Template(ricon.png)) touch((pos[0], pos[1] 50)) # 在图标下方50像素处点击9.3 滑动不生效python复制# 问题swipe没反应 # 可能原因起点和终点太近、duration太短 # 解决增大滑动距离增加duration swipe((0, 0.8), (0, 0.2), duration1.0) # 大幅滑动持续1秒 # 连续滑动对付惯性滚动 for _ in range(3): swipe((0, 0.7), (0, 0.3), duration0.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滑到p2swipe(p1, v)从p1按向量v滑动输入text(内容)输入文本keyevent(HOME)按键事件等待sleep(N)固定等待N秒wait(img, timeoutN)智能等待推荐exists(img)判断是否存在断言assert_exists(img, msg)验证存在assert_not_exists(img, msg)验证不存在应用start_app(pkg)启动APPstop_app(pkg)停止APPclear_app(pkg)清除APP数据截图snapshot()截图保存到报告图像识别核心口诀截图要小不要大特征明显好匹配。 阈值默认零点七找不到就降一降。 动态内容要避开时间电量不能截。 多设备跑用坐标图像识别作备份。 wait比sleep更智能exists判断不报错。 断言写在操作后报告清晰好排查。下一篇预告Airtest自动化测试专题第三篇——「Poco控件识别精准定位UI元素」掌握Poco的控件树定位、层级选择、等待与断言以及AirtestPoco混合编程。本文约5500字是「Airtest自动化测试专题」第2篇。建议打开AirtestIDE跟着示例敲一遍印象最深。