Airtest+Poco 自动化测试实战:从环境搭建到复杂场景脚本编写
1. 为什么你需要AirtestPoco一个测试工程师的真心话如果你正在为移动应用或游戏的自动化测试发愁每天重复着点点点、录屏、截图、对比的枯燥工作或者被那些动态UI、不规则按钮、复杂交互流程折磨得够呛那么今天这篇文章就是为你准备的。我叫老张在AI和智能硬件测试这行摸爬滚打了十多年从早期的按键精灵到后来的Appium、UiAutomator几乎把市面上的自动化工具都折腾了个遍。最后我选择把AirtestPoco作为团队的主力测试框架不是因为它最完美而是因为它最能解决实际问题尤其是对新手和中小团队来说上手快、成本低、见效猛。简单来说Airtest和Poco是网易开源的一对“黄金搭档”。Airtest主打基于图像识别的跨平台自动化你截张图它就能在屏幕上找到并点击特别适合游戏里那些花里胡哨、没有固定控件的按钮。而Poco则是基于UI控件树的精准定位工具像原生APP、Unity/Cocos游戏里的标准按钮、文本框它都能像查户口一样精确找到。把它们俩结合起来就相当于你同时拥有了“人眼”和“X光透视眼”无论是静态的列表项还是动态飘过的特效都能轻松搞定。我见过太多团队在自动化测试的起步阶段就栽了跟头环境配置复杂、脚本维护成本高、学习曲线陡峭。AirtestPoco最大的好处就是低代码甚至无代码。它提供了AirtestIDE这个图形化工具你甚至不用写一行代码通过录制回放就能完成简单测试。对于开发者它又是纯Python的可以无缝集成到你的CI/CD流水线里。接下来我就带你从零开始一步步搭建环境写出能应对各种复杂场景的健壮脚本。2. 从零开始手把手搭建你的第一个测试环境万事开头难但把环境配好就成功了一半。这里我会把每一步都拆开揉碎了讲确保你一次成功避开我当年踩过的那些坑。2.1 核心工具安装告别依赖地狱首先我们需要两个核心工具AirtestIDE和Python环境。我强烈建议新手直接从AirtestIDE开始它把所有东西都打包好了。第一步安装AirtestIDE直接去Airtest官网下载对应操作系统的安装包。Windows用户就下.exemacOS用户下.dmg。安装过程就是一路“下一步”没什么好说的。安装完成后打开你会看到一个集成了代码编辑器、设备窗口、脚本运行和报告查看器的界面非常清爽。第二步准备Python环境虽然AirtestIDE自带Python但如果你想在命令行或者自己的项目里运行脚本就需要一个独立的Python环境。我推荐使用Python 3.7到3.9版本兼容性最好。打开你的终端Windows用CMD或PowerShellmacOS/Linux用Terminal用pip安装核心库pip install airtest pocoui这里有个小技巧如果你遇到网络问题可以使用国内的镜像源加速比如清华源pip install airtest pocoui -i https://pypi.tuna.tsinghua.edu.cn/simple安装完成后可以写个最简单的脚本来验证from airtest.core.api import * print(“Airtest环境检查通过”)运行没报错说明基础环境OK了。2.2 连接你的设备安卓、iOS和Windows环境好了接下来就得让脚本能操作你的手机或模拟器。这是最关键的一步连接不上后面全是白搭。2.2.1 连接Android真机最常用开启USB调试在你的安卓手机上进入“设置” - “关于手机”连续点击“版本号”7次开启“开发者选项”。然后进入“开发者选项”打开“USB调试”。连接电脑用USB数据线连接手机和电脑。如果是Windows可能需要安装对应的手机驱动一般各大手机官网有提供。在AirtestIDE中连接打开AirtestIDE右侧你会看到“设备连接”面板。点击Refresh ADB按钮。如果一切正常你的设备序列号会出现在列表中。如果没出现试试点击Restart ADB重启ADB服务。看到设备后点击Connect按钮。连接成功后设备屏幕会实时投射到IDE中。注意部分手机如华为、小米在连接时可能需要额外授权“允许USB调试”的弹窗一定要在手机上点击“允许”。如果反复连接失败可以尝试换一根数据线或者检查电脑的ADB版本是否过旧。2.2.2 连接Android模拟器模拟器如夜神、雷电、MuMu的连接原理和真机类似但需要知道模拟器的ADB端口。通常模拟器会运行一个独立的ADB服务。启动你的模拟器。在AirtestIDE的设备连接面板点击Refresh ADB。如果没刷出来你可能需要手动连接。打开终端使用命令连接以夜神模拟器默认端口62001为例adb connect 127.0.0.1:62001连接成功后再回到AirtestIDE点击Refresh ADB和Connect。2.2.3 连接iOS设备连接iOS设备相对麻烦一些需要一台macOS电脑和Xcode。在mac上安装Xcode。通过Airtest官网文档安装ios-tagent到你的iPhone上这通常需要Apple开发者账号或使用免费的个人证书。确保iPhone和Mac在同一个Wi-Fi网络下获取iPhone的IP地址。在AirtestIDE的设备连接字符串中输入iOS:///你的iPhone IP地址:8100然后连接。2.2.4 连接Windows应用窗口测试PC上的游戏或应用也很方便。在AirtestIDE设备面板点击“选择游戏画面”按钮然后用鼠标框选你要测试的应用程序窗口即可。Airtest会将其嵌入到IDE中。2.3 编写你的第一个脚本让手机自己动起来环境设备都准备好了我们来点实际的。打开AirtestIDE新建一个.air脚本文件。我们写一个最简单的脚本打开手机上的设置应用假设是安卓。# -*- encodingutf8 -*- __author__ “你的名字” from airtest.core.api import * # 导入Airtest所有核心API # 1. 自动连接设备如果IDE已连接这里可以省略 # auto_setup(__file__) # 2. 唤醒设备屏幕防止黑屏 wake() # 3. 回到主屏幕确保我们从干净的状态开始 home() sleep(1.0) # 等待1秒让动画完成 # 4. 启动“设置”应用 # 你需要先知道“设置”应用的包名通常是 com.android.settings start_app(“com.android.settings”) sleep(2.0) # 等待应用启动 # 5. 使用图像识别点击“WLAN”选项这里需要你先截图 # 在AirtestIDE里你可以使用截图工具截取“WLAN”文字的图片命名为wlan.png if exists(Template(r”wlan.png”)): touch(Template(r”wlan.png”)) print(“成功点击WLAN设置”) else: print(“未找到WLAN设置入口”) # 6. 简单的断言检查是否进入了WLAN页面 # 我们可以通过判断某个特征图像是否存在来断言 assert_exists(Template(r”wifi_switch.png”), “未成功进入WLAN设置页面”) print(“测试通过”)把这段代码复制到AirtestIDE里确保你的手机已经连接然后点击运行按钮。你会看到脚本自动操作你的手机打开设置点击WLAN。这就是自动化测试的魔力3. Airtest图像识别实战让脚本拥有“眼睛”Airtest的核心能力就是图像识别。它不像传统基于坐标的脚本那样脆弱屏幕分辨率一变就失效而是通过比较当前屏幕和你预先截取的模板图片来定位元素。这就像给你的脚本装上了一双“眼睛”。3.1 核心操作API点击、滑动、输入touch()– 万能点击手touch是使用频率最高的操作。它不仅可以点击图片还能点击绝对坐标或相对坐标。# 点击一张图片最常用 touch(Template(r”button_ok.png”)) # 点击绝对坐标 (x, y) touch((500, 1000)) # 点击相对坐标 (0.5, 0.5) 代表屏幕正中心 touch((0.5, 0.5)) # 长按2秒某些游戏需要长按操作 touch(Template(r”skill_icon.png”), duration2)这里的关键是Template对象。当你用AirtestIDE截图时它会自动生成这个对象里面包含了图片路径、记录时的屏幕分辨率(resolution)和图片在屏幕中的相对位置(record_pos)。正是这些信息让Airtest能在不同分辨率的设备上找到你的目标。swipe()– 模拟手指滑动列表滚动、页面切换都离不开滑动。# 从屏幕底部向上滑动常见的上拉刷新 swipe((0.5, 0.8), (0.5, 0.2), duration0.8) # 从一张图片滑动到另一张图片 swipe(Template(r”item_start.png”), Template(r”item_end.png”)) # 使用矢量滑动知道方向和距离 swipe((0.5, 0.5), vector(0, -0.5)) # 从中心向上滑动半个屏幕duration参数控制滑动速度值越大滑动越慢越平滑。对于游戏中的精准操作如抽卡、瞄准调整这个参数很重要。text()– 文本输入专家自动登录、搜索功能都需要输入文本。# 先点击输入框激活光标 touch(Template(r”input_box.png”)) sleep(0.5) # 等待输入法弹出 # 输入文本并按下回车enterTrue是默认的 text(“Hello Airtest”, enterTrue) # 输入文本但不回车比如输入验证码 text(“123456”, enterFalse) # 然后手动点击“确定”按钮 touch(Template(r”confirm_btn.png”))注意不同手机的输入法可能行为不同。如果text输入不成功可以尝试组合使用touch点击虚拟键盘或者使用keyevent模拟键盘事件。3.2 等待与断言让脚本更智能、更可靠一个健壮的脚本不能像愣头青一样横冲直撞必须学会“等待”和“判断”。wait()– 耐心等待目标出现网络加载、页面跳转都需要时间。wait会不断查找目标直到找到或超时。# 等待“登录成功”的提示出现最多等10秒 success_icon wait(Template(r”login_success.png”), timeout10) if success_icon: print(“登录成功”) else: print(“登录超时可能失败了。”)exists()– 检查元素是否存在用于条件判断比wait更轻量只检查一次。# 检查是否有更新弹窗 if exists(Template(r”update_dialog.png”)): touch(Template(r”update_later.png”)) # 点击“稍后更新” print(“已关闭更新弹窗”)断言 – 测试的灵魂断言是自动化测试的校验点用于确认结果是否符合预期。from airtest.core.assertions import * # 断言某个图片必须存在否则测试失败 assert_exists(Template(r”welcome_page.png”), “应用启动后未显示欢迎页”) # 断言两个值相等常用于校验文本内容 # 假设我们通过Poco获取了一个文本控件的内容 # actual_text poco(“title”).get_text() # assert_equal(actual_text, “预期标题”, “标题显示不正确”) # 断言表达式为真 assert_true(1 1 2, “基本的数学运算出错了”)断言失败时Airtest会标记测试步骤为失败并在最终的HTML报告中高亮显示非常利于问题定位。3.3 图像识别进阶技巧与避坑指南直接用基础API可能会遇到一些问题比如找图慢、匹配不准。下面分享几个我实战中总结的“黑科技”。技巧一调整识别阈值平衡速度与精度Template对象有一个threshold参数默认是0.770%相似度。对于清晰、固定的图标可以调高如0.9以减少误匹配对于动态、模糊的目标如游戏技能特效可以调低如0.6以提高容错。# 高精度匹配登录按钮 login_btn Template(r”login_btn.png”, threshold0.9) # 低精度匹配闪烁的奖励图标 reward_icon Template(r”reward.png”, threshold0.6)技巧二全局配置一劳永逸如果你觉得每张图都设置threshold太麻烦可以修改全局设置。from airtest.core.api import * ST.THRESHOLD 0.8 # 将全局默认阈值设为0.8 ST.FIND_TIMEOUT 30 # 将全局查找超时设为30秒这样脚本中所有没有单独设置阈值的图片都会使用0.8这个标准。技巧三局部找图大幅提升效率全屏找图就像大海捞针非常慢。如果我们知道目标大概在屏幕的哪个区域可以限定搜索范围。from airtest.aircv import * # 1. 先截取当前屏幕 screen G.DEVICE.snapshot() # 2. 定义搜索区域 (左上角x, 左上角y, 右下角x, 右下角y) search_area (100, 200, 500, 800) # 假设目标只在屏幕中间这块区域 # 3. 裁剪出这个区域 part_screen aircv.crop_image(screen, search_area) # 4. 在局部图片中找图 template Template(r”target.png”) pos template.match_in(part_screen) if pos: # 注意pos是相对于局部图片的坐标需要转换回全屏坐标 global_x pos[0] search_area[0] global_y pos[1] search_area[1] touch((global_x, global_y))这个方法在遍历列表项、查找状态栏图标时特别有用能将找图速度提升好几倍。避坑处理动态UI和透明背景游戏UI经常有半透明、发光、动画效果这会让图像识别很不稳定。我的经验是截图时机尽量在动画静止时截图。可以加个sleep等待动画结束。截图内容截取图标最具辨识度的核心部分避开半透明的边缘和动态特效。启用RGB匹配如果图标颜色是关键特征可以设置rgbTrue进行三通道颜色匹配但要注意不同设备可能有色差。Template(r”colorful_icon.png”, rgbTrue, threshold0.7)4. Poco控件定位实战直达UI的“骨骼”当你的应用或游戏有标准的UI控件树时比如Android原生APP、Unity/UE4游戏Poco就是比Airtest图像识别更优的选择。它不靠“看”而是直接访问应用的UI结构定位精准、速度极快且不受屏幕分辨率、皮肤主题的影响。4.1 初始化Poco连接你的应用使用Poco前必须确保你的应用已经“暴露”了UI树。对于原生Android/iOS应用系统自带支持。对于游戏需要接入对应的Poco-SDKUnity、Cocos、UE4都有官方SDK。from airtest.core.api import * from poco.drivers.android.uiautomation import AndroidUiautomationPoco # 连接设备 connect_device(“Android:///”) sleep(2) # 等待设备稳定 # 初始化Android原生Poco # use_airtest_inputTrue 让Poco使用Airtest的输入法这样操作会被记录到报告中 # screenshot_each_actionFalse 关闭每个动作都截图避免报告过于臃肿 poco AndroidUiautomationPoco(use_airtest_inputTrue, screenshot_each_actionFalse) # 对于Unity游戏初始化方式不同 # from poco.drivers.unity3d import UnityPoco # poco UnityPoco()初始化成功后你就可以像操作对象一样操作屏幕上的UI元素了。4.2 三大选择器精准定位控件的“法宝”Poco提供了三种强大的选择器来定位控件你需要根据实际情况灵活组合。4.2.1 基本选择器按属性直接找这是最直接的方式通过控件的name、text、type等属性来定位。# 点击一个name为“loginBtn”的按钮 poco(“loginBtn”).click() # 点击文本内容为“登录”的控件 poco(text“登录”).click() # 定位类型为Button且文本为“提交”的控件 poco(type“Button”, text“提交”).click() # 使用正则表达式匹配对付动态生成的控件名 # 例如匹配所有以“item_”开头的控件 poco(nameMatches“item_.*”)[0].click() # 点击第一个4.2.2 相对选择器通过“关系”找当控件没有唯一属性时可以通过它与其他控件的关系来定位。# 假设有一个列表每个列表项结构类似 # - ListView # - Item1 (type“ListItem”) # - Title (text“新闻1”) # - Button (name“like”) # - Item2 # ... # 1. 查找ListView下所有类型为ListItem的子节点 items poco(“ListView”).child(type“ListItem”) for item in items: print(item.child(“Title”).get_text()) # 获取每个Item的标题 # 2. 查找“新闻1”这个标题的兄弟节点旁边的点赞按钮 like_btn poco(text“新闻1”).sibling(name“like”) like_btn.click() # 3. 查找某个按钮的所有后代节点中的文本控件 all_texts poco(“container”).offspring(type“Text”)child找直接儿子offspring找所有子孙sibling找兄弟parent找爸爸。这套“家族关系”语法非常直观。4.2.3 空间顺序选择器当所有属性都相同时最常见的就是列表每一项的控件属性完全一样。这时只能用索引下标来区分。# 获取一个列表的所有项 song_list poco(“SongList”).child(type“ListItem”) print(f“列表共有 {len(song_list)} 首歌”) # 点击第一首歌 song_list[0].click() sleep(1) # 点击第三首歌 song_list[2].click() # 遍历列表 for index, song in enumerate(song_list): song_name song.child(“name”).get_text() print(f“第{index1}首: {song_name}”) if “我喜欢的歌” in song_name: song.click() break4.3 控件操作与属性获取不仅仅是点击定位到控件后你可以对它做很多事情。基础操作# 点击 poco(“btn_ok”).click() # 长按默认2秒 poco(“avatar”).long_click() # 长按5秒 poco(“delete_btn”).long_click(duration5) # 双击 poco(“icon”).double_click() # 输入文本前提是控件是可输入的 poco(“input_username”).set_text(“testuser”) # 滑动控件相对于控件本身 poco(“slider”).swipe(“right”) # 向右滑动 poco(“scroll_view”).swipe(“up”) # 向上滑动列表滚动获取控件状态和信息自动化测试不仅仅是操作更是验证。Poco可以轻松获取控件的各种属性。# 获取文本内容 title poco(“title”).attr(“text”) print(f“标题是{title}”) # 获取控件名称、类型 ctrl_name poco(“widget”).attr(“name”) ctrl_type poco(“widget”).attr(“type”) # 获取控件在屏幕上的位置和大小返回的是归一化坐标0~1之间 pos poco(“button”).get_position() # 例如 [0.5, 0.5] 表示中心 size poco(“button”).get_size() # 例如 [0.1, 0.05] 表示宽高占屏幕比例 # 判断控件是否存在返回True/False if poco(“loading”).exists(): print(“正在加载等待...”) poco(“loading”).wait_for_disappearance(timeout10) # 等待加载圈消失 else: print(“加载完成”)高级等待让脚本更稳定wait_for_appearance和wait_for_disappearance是我最爱的两个方法它们会智能地等待某个控件出现或消失超时则报错非常适合用于同步操作。# 等待登录按钮出现最多等15秒 poco(“loginBtn”).wait_for_appearance(timeout15) poco(“loginBtn”).click() # 输入密码后等待“登录中...”的提示消失最多等10秒 poco(text“登录中...”)。wait_for_disappearance(timeout10) # 等待多个必要控件都出现 poco.wait_for_all([poco(“welcome”), poco(“menu”), poco(“profile”)])5. 复杂场景实战组合拳解决真实难题掌握了基础我们来挑战一些真实项目中常见的复杂场景。这些场景往往需要Airtest和Poco混合使用取长补短。5.1 场景一游戏新手引导流程自动化游戏的新手引导UI变化多端既有固定的箭头指示适合Poco也有高亮、闪烁的动画效果适合Airtest图像识别。def newbie_guide(): “”“自动化完成游戏新手引导”“” # 步骤1等待并点击“开始游戏”按钮用图像识别因为可能是特效按钮 start_btn wait(Template(r”guide_start.png”), timeout20) touch(start_btn) sleep(3) # 等待剧情动画 # 步骤2使用Poco点击对话气泡控件稳定 while poco(textMatches“.*…”).exists(): # 匹配包含省略号的文本 poco(textMatches“.*…”).click() sleep(1) # 步骤3教学战斗识别并点击闪烁的技能图标图像识别 skill_icon Template(r”guide_skill.png”, threshold0.6) # 阈值调低适应闪烁 wait(skill_icon, timeout10) touch(skill_icon) sleep(2) # 步骤4拖动角色移动Airtest滑动 swipe((0.3, 0.7), (0.7, 0.7), duration1.0) # 横向拖动角色 sleep(1) # 步骤5领取奖励使用Poco精准点击宝箱 # 假设宝箱控件名为“chest”但可能有很多个用相对选择器找第一个 poco(“MainUI”).child(“chest”)[0].click() # 等待奖励弹窗出现 poco(“reward_window”).wait_for_appearance(timeout5) # 点击弹窗内的“确定”按钮 poco(“reward_window”).child(“ok_btn”).click() print(“新手引导流程自动化完成”)5.2 场景二列表数据校验与翻页测试一个新闻APP的列表页需要滚动加载并校验每条新闻的标题和图片是否正常显示。def check_news_list(): “”“校验新闻列表”“” news_container poco(“NewsList”) # 列表容器 last_news_title “” for page in range(3): # 检查3页数据 print(f“正在检查第 {page 1} 页...”) # 获取当前屏幕内所有新闻项 # 注意Poco默认只获取当前渲染到屏幕上的控件 news_items news_container.child(type“NewsItem”) for item in news_items: # 获取新闻标题 title item.child(“title”).get_text() # 简单去重防止因滚动导致同一项被多次检查 if title last_news_title: continue last_news_title title # 断言标题不为空 assert_true(title and len(title.strip()) 0, f“新闻标题为空: {item}”) # 检查新闻图片是否存在通过Poco判断图片控件是否渲染 # 假设图片控件是一个Image类型我们可以检查其是否“存在” if item.child(type“Image”).exists(): # 还可以用Airtest截图检查图片区域是否为灰色加载失败 # 这里需要结合图像识别是一个更复杂的例子 pass else: print(f“警告新闻‘{title}’的图片可能未加载”) # 滚动到下一页列表底部 # 先尝试用Poco的swipe滚动容器 news_container.swipe(“up”) # 或者使用Airtest的swipe在屏幕特定区域滑动 # swipe((0.5, 0.8), (0.5, 0.2), duration0.5) sleep(2) # 等待新内容加载 # 可选检查“加载中”状态是否消失 if poco(text“加载中…”).exists(): poco(text“加载中…”).wait_for_disappearance(timeout10) print(“新闻列表校验通过”)5.3 场景三处理权限弹窗和系统对话框安装新APP或首次使用某些功能时系统会弹出权限申请。这些弹窗是系统级的通常无法用Poco定位必须用Airtest图像识别。def handle_permission_popups(): “”“自动处理常见的安卓权限弹窗”“” # 定义一个权限弹窗的图片模板列表 permission_windows [ Template(r”allow_while_using_app.png”), # “使用时允许” Template(r”allow_only_this_time.png”), # “仅本次允许” Template(r”deny.png”), # “拒绝” Template(r”ok_button.png”), # 通用确定按钮 Template(r”continue_button.png”), # 继续按钮 ] # 设置一个超时避免无限循环 start_time time.time() timeout 30 # 最多处理30秒 while time.time() - start_time timeout: # 使用Airtest的exists检查是否有弹窗 for window in permission_windows: pos exists(window) if pos: print(f“检测到弹窗: {window.filename}”) # 通常我们点击“允许”或“确定” if “deny” not in window.filename: touch(pos) sleep(1) # 等待弹窗消失 else: # 如果是拒绝按钮我们可能想点其他位置或者记录日志 print(“遇到拒绝按钮需要根据测试用例决定操作”) break # 处理一个弹窗后跳出for循环重新扫描 else: # 如果for循环正常结束没找到任何弹窗说明当前没有弹窗等待一小会儿再检查 sleep(0.5) continue # 如果找到了弹窗并处理了立即重新开始扫描因为可能连续弹出多个 continue print(“权限弹窗处理完毕或超时。”)5.4 场景四使用freeze()大幅提升遍历效率当你需要遍历一个非常大的UI树比如游戏里的背包有上百个物品时每次操作都重新获取整个UI树会非常慢。freeze()方法可以将当前的UI树“冻结”一份在内存中后续操作都基于这份快照速度极快。def check_inventory(): “”“快速检查背包所有物品”“” # 假设背包打开后物品网格的父节点是“InventoryGrid” with poco.freeze() as frozen_poco: # 进入冻结上下文 # 这行代码只会在开始时获取一次完整的UI树 all_items frozen_poco(“InventoryGrid”).child(type“InventoryItem”) print(f“背包中共有 {len(all_items)} 件物品”) for item in all_items: # 在冻结状态下这些属性获取操作不会再次请求手机速度飞快 item_name item.child(“name”).get_text() item_count item.child(“count”).get_text() print(f“物品: {item_name}, 数量: {item_count}”) # 注意冻结期间如果UI发生了变化比如你点击了某个物品使其消失 # 那么快照不会更新后续操作可能会出错。 # 所以冻结只适用于静态或变化已知的界面。重要提醒freeze()是一把双刃剑。它牺牲了实时性换来了速度。在冻结期间如果屏幕UI发生了变化比如你点击了一个按钮弹出了新窗口冻结的Poco对象是不知道的它仍然操作的是旧的UI树这会导致操作失败或点错地方。所以它最适合用于静态页面的只读遍历操作。6. 脚本优化与报告生成让自动化真正可用写出来的脚本能跑只是第一步跑得稳、看得清结果才是关键。6.1 脚本健壮性优化1. 增加智能等待避免“硬休眠”不要到处写死板的sleep(10)。使用wait、exists和Poco的wait_for_appearance来替代。# 不好的做法 touch(login_btn) sleep(10) # 万一网络慢10秒不够呢万一网络快又白等了呢 assert_exists(success_icon) # 好的做法 touch(login_btn) # 等待成功图标出现最多等20秒每隔0.5秒检查一次 success_pos wait(success_icon, timeout20, interval0.5) if success_pos: print(“登录成功”) else: print(“登录失败”) # 可以在这里截图记录失败现场 snapshot(filename“login_failed.png”)2. 使用try-except处理异常网络波动、临时弹窗、资源加载失败都会导致脚本中断。合理的异常捕获能让脚本继续执行或优雅失败。def safe_click(element): “”“安全的点击操作包含重试机制”“” for i in range(3): # 重试3次 try: if isinstance(element, Template): # Airtest图片 pos exists(element) if pos: touch(pos) return True else: # Poco控件 if element.exists(): element.click() return True sleep(1) # 本次没找到等1秒再试 except Exception as e: print(f“第{i1}次点击尝试失败: {e}”) snapshot(filenamef”click_error_retry_{i}.png”) sleep(2) print(f“元素点击失败: {element}”) return False # 使用方式 safe_click(poco(“unstable_button”)) safe_click(Template(r”flaky_icon.png”))3. 封装通用操作和页面对象将常用操作如登录、退出和页面元素定位封装成函数或类让主脚本清晰易懂。class LoginPage: def __init__(self, poco): self.poco poco self.username_input poco(“com.example.app:id/username”) self.password_input poco(“com.example.app:id/password”) self.login_button poco(“com.example.app:id/login”) def login(self, username, password): self.username_input.set_text(username) self.password_input.set_text(password) self.login_button.click() # 等待登录完成 return poco(“main_page”).wait_for_appearance(timeout10) # 在主脚本中使用 login_page LoginPage(poco) if login_page.login(“testuser”, “password123”): print(“登录成功进入主页面”)6.2 生成直观的测试报告Airtest内置了非常棒的HTML报告生成功能能清晰展示每一步操作、截图和结果。from airtest.report.report import simple_report import datetime # 在脚本开头记录开始时间auto_setup会自动做这件事 # auto_setup(__file__, logdirTrue) # 启用日志记录 # … 这里是你的测试脚本 … # 在脚本最后生成报告 if __name__ “__main__”: try: # 运行你的测试函数 run_all_tests() finally: # 无论测试成功还是失败都生成报告 report_time datetime.datetime.now().strftime(“%Y%m%d_%H%M%S”) report_path f”./reports/report_{report_time}.html” # logpathTrue 表示使用auto_setup设置的日志目录 # output 指定报告输出路径 simple_report(__file__, logpathTrue, outputreport_path, logfile“log.txt”, # 指定日志文件默认为log.txt lang‘zh’) # 报告语言设置为中文 print(f“测试报告已生成: {report_path}”)生成的报告会包含一个时间线清晰展示每个步骤做了什么点击了哪里、输入了什么、对应的屏幕截图、以及步骤的成功失败状态。这对于调试和结果回溯 invaluable。6.3 集成到CI/CD流水线要让自动化测试创造最大价值就必须把它集成到持续集成流程中。你可以使用命令行来运行Airtest脚本。# 基础命令 airtest run “你的测试脚本.air” --device Android:///手机设备ID --log ./logs # 更详细的命令设置报告输出和日志级别 airtest run “test_login.air” --device Android:/// --log ./test_log --export ./test_report --compress 80在Jenkins、GitLab CI等工具中你可以配置一个Job在每次代码提交后自动执行这些命令并收集测试报告。如果测试失败可以自动通知开发人员。走到这里你已经从一个自动化测试的新手成长为能够利用AirtestPoco解决实际复杂测试场景的实践者了。这套组合拳的强大之处在于它的灵活性和实用性——图像识别应对变化控件定位保证精准两者互补覆盖了绝大多数自动化测试的需求。我自己的团队用它来测试从手机APP到大型3D游戏的各种项目维护成本比传统工具体系低了不止一个量级。当然没有银弹。你可能会遇到图像识别在低对比度场景下失灵或者Poco在某些深度定制的Android系统上无法获取控件树的情况。这时候就需要你结合具体问题灵活运用今天讲到的技巧比如调整识别阈值、结合坐标操作、或者甚至调用更底层的ADB命令。自动化测试脚本的编写三分靠工具七分靠设计和经验。多写多调试多总结你会发现自己构建起来的自动化测试体系正在实实在在地提升整个团队的开发效率和产品质量。

相关新闻

Coze工作流实战:从链式到并行,解锁四大核心模式的应用密码

Coze工作流实战:从链式到并行,解锁四大核心模式的应用密码

1. 链式工作流:像流水线一样精准执行 我刚开始接触Coze工作流的时候,最让我感到踏实的其实就是链式工作流。它特别像我们小时候玩的传话游戏,或者更贴切地说,像一条设计精密的工业流水线。每个环节(节点)都…

2026/7/7 9:58:00 阅读更多 →
WIFI芯片选型实战:从场景需求倒推二次开发最优解

WIFI芯片选型实战:从场景需求倒推二次开发最优解

1. 从“我要做个啥”开始:聊聊选型思维的转变 干了这么多年嵌入式开发,我经手过的WIFI项目少说也有几十个了。早年选型,我和很多人一样,喜欢先扎进芯片手册和参数对比表里,看谁的主频高、谁的RAM大、谁的接口多。结果呢…

2026/7/4 19:10:19 阅读更多 →
昇腾NPU用户必看:GPUStack集成MindIE引擎的性能对比与优化指南

昇腾NPU用户必看:GPUStack集成MindIE引擎的性能对比与优化指南

昇腾NPU实战:MindIE引擎深度集成GPUStack的性能飞跃与调优全解析 如果你手头有昇腾910B或310P的硬件,并且正在寻找一个既能充分发挥硬件潜力,又能简化部署运维的模型服务平台,那么GPUStack与MindIE引擎的结合,绝对是你…

2026/7/5 1:08:26 阅读更多 →

最新新闻

3分钟搞定:让经典游戏在现代Windows重获新生的终极方案

3分钟搞定:让经典游戏在现代Windows重获新生的终极方案

3分钟搞定:让经典游戏在现代Windows重获新生的终极方案 【免费下载链接】ipxwrapper 项目地址: https://gitcode.com/gh_mirrors/ip/ipxwrapper 还在为《星际争霸》、《暗黑破坏神》这些经典游戏无法在Windows 10/11上联机而烦恼吗?&#x1f914…

2026/7/7 9:58:37 阅读更多 →
Subtitle Edit:免费开源字幕编辑工具,5大场景化解决方案助你高效制作专业字幕

Subtitle Edit:免费开源字幕编辑工具,5大场景化解决方案助你高效制作专业字幕

Subtitle Edit:免费开源字幕编辑工具,5大场景化解决方案助你高效制作专业字幕 【免费下载链接】subtitleedit the subtitle editor :) 项目地址: https://gitcode.com/gh_mirrors/su/subtitleedit Subtitle Edit是一款功能强大的免费开源字幕编辑…

2026/7/7 9:58:37 阅读更多 →
Windows 7终极兼容方案:让Blender 3.x+在老系统上完美运行

Windows 7终极兼容方案:让Blender 3.x+在老系统上完美运行

Windows 7终极兼容方案:让Blender 3.x在老系统上完美运行 【免费下载链接】BlenderCompat Windows 7 support for Blender 3.x and newer 项目地址: https://gitcode.com/gh_mirrors/bl/BlenderCompat 还在为Windows 7无法运行最新版Blender而烦恼吗&#xf…

2026/7/7 9:56:37 阅读更多 →
铝箔包装袋给袋机防双袋解决方案——磁感应双张检测器

铝箔包装袋给袋机防双袋解决方案——磁感应双张检测器

一、铝箔袋给袋机的双袋难题1.1 为什么铝箔袋特别容易出现双袋铝箔复合包装袋(铝箔PE/PET多层复合结构)在给袋机上运行时,双袋问题比普通过渡袋更突出,原因有三个:静电吸附严重。 铝箔袋在制袋、运输、存放过程中&…

2026/7/7 9:54:36 阅读更多 →
find命令高级用法:动作执行与xargs协同实战

find命令高级用法:动作执行与xargs协同实战

引言:find不仅是搜索,更是行动引擎 Linux 系统中的文件数量庞大,find 命令的价值不仅在于定位文件,更在于对匹配结果执行一系列操作。本文从动作执行入手,逐步过渡到 xargs 协同,并通过完整的 playground 实例演示批量修改权限与时间戳的真实场景,帮助你从“会用”进阶…

2026/7/7 9:52:36 阅读更多 →
IPXWrapper终极指南:3步让经典游戏在Windows 10/11完美联机

IPXWrapper终极指南:3步让经典游戏在Windows 10/11完美联机

IPXWrapper终极指南:3步让经典游戏在Windows 10/11完美联机 【免费下载链接】ipxwrapper 项目地址: https://gitcode.com/gh_mirrors/ip/ipxwrapper 还在为《红色警戒2》、《星际争霸》、《暗黑破坏神》这些经典游戏无法在现代Windows系统上联机而烦恼吗&am…

2026/7/7 9:52:36 阅读更多 →

日新闻

鸿蒙新特性:图片画廊与轮播导航——构建沉浸式图片浏览体验

鸿蒙新特性:图片画廊与轮播导航——构建沉浸式图片浏览体验

图片浏览是移动应用中最高频的场景之一。从社交应用的照片流到电商平台的商品图集,从旅游应用的景点相册到摄影作品展示——用户对图片浏览的体验要求不断提高:流畅的切换动画、直观的缩略图导航、便捷的收藏操作、自动播放模式。HarmonyOS NEXT ArkUI 虽…

2026/7/7 0:05:16 阅读更多 →
24V DC-DC降压芯片PW2312B/PW2815,SOT23-6到SOP8-EP方案对比

24V DC-DC降压芯片PW2312B/PW2815,SOT23-6到SOP8-EP方案对比

24V稳压芯片完整选型指南 PW8600 PW75XX PW2815 PW2312B LDODC/DC全方案 一、24V稳压方案概述 24V直流电源在工业自动化、门禁系统、电梯控制、汽车电子、LED驱动、监控设备等场景中应用极广,是最常见的中压直流母线电压。要将24V母线稳定降压至下游MCU、传感器…

2026/7/7 0:05:16 阅读更多 →
RAG+知识图谱混合检索与Graph RAG核心对比

RAG+知识图谱混合检索与Graph RAG核心对比

做企业RAG落地的团队,往往容易卡在一容易踩坑的选型难题: 当需求单纯靠向量RAG搞不定、单纯靠知识图谱也搞不定,必须同时依赖「文本语义理解 实体关系推理」时,到底是做「向量图谱混合检索」就够了,还是必须上「Grap…

2026/7/7 0:07:19 阅读更多 →

周新闻

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容 【免费下载链接】BiliTools A cross-platform bilibili toolbox. 跨平台哔哩哔哩工具箱,支持下载视频、番剧等等各类资源 项目地址: https://gitcode.com/GitHub_Trending/bilit/BiliTools …

2026/7/6 8:11:50 阅读更多 →
威胁模型全解析:从新手入门到实战应用,助你构建安全产品!

威胁模型全解析:从新手入门到实战应用,助你构建安全产品!

威胁模型的陌生现状在忙碌疲惫的一天里,参与了关于混合后量子密码学的讨论,应付端点攻击找茬的人,还参与留言板讨论后,发现“威胁模型”对多数人仍是陌生概念,且多被当作时髦用语。有趣的相关画作有一幅由 Embyr 创作的…

2026/7/6 8:11:52 阅读更多 →
渗透测试入门指南:从零基础到实战环境搭建

渗透测试入门指南:从零基础到实战环境搭建

1. 从“看热闹”到“入门”:我理解的渗透测试到底是什么?每次看到新闻里说某个大公司的数据被“黑”了,或者某个网站被攻击导致服务瘫痪,你是不是和我一样,心里会冒出两个念头:一是“这黑客真厉害”&#x…

2026/7/6 6:52:56 阅读更多 →

月新闻