做iOS自动化测试的朋友这几天应该都被iOS 17折腾得够呛。明明上个月还跑得好好的脚本换台iOS 17的真机直接罢工Xcode编译报错、WDA启动闪退、Airtest连不上——这些坑我几乎全踩了一遍。这篇文章就围绕iOS-Tagent升级适配iOS 17的完整过程把WDA部署和Airtest适配涉及的原理、步骤、参数和避坑经验一次性讲透适合正在做iOS UI自动化、或者准备把老设备升级到iOS 17的测试同学参考。1. 为什么iOS 17让旧版自动化方案集体翻车1.1 iOS 17到底改了什么东西先说结论iOS 17并不是简单加了几个新API而是动了很多底层权限和XCTest运行机制的细节。这导致以前编译一次到处跑的思路直接失效。作为一个自动化测试框架WDAWebDriverAgent基于XCTest封装的WebDriver协议实现本质上是用XCTestRunner容器在真机上跑一套HTTP服务。iOS 17对XCTest运行时的沙盒限制、权限校验和签名校验都做了收紧具体表现为旧版WDA编译产物装进iOS 17真机后测试Runner一启动就闪退日志里常见Test runner failed to launch这类错误。系统设置里开发者模式成为硬性开关不开启的话XCUITest根本无法正常拉起被测App。部分访问相册、定位、通讯录等系统权限的弹窗在iOS 17上文案和处理路径变了脚本里如果没做针对性处理经常会卡在弹窗上。这些变化对自动化链路的影响是连锁的WDA跑不起来Airtest就没办法通过它拿到页面层级和执行点击WDA偶尔能起来但元素快照超时、截图黑屏等问题也层出不穷。所以升级iOS 17之后不能只换某个单点工具而是要把整个链路都拉到适配版本上。1.2 iOS-Tagent和官方WDA的定位差异很多同学不明白iOS-Tagent和WDA到底是什么关系。简单说WDA是上游开源的iOS自动化服务iOS-Tagent是基于它做二次开发和定制的一套分支专门适配Airtest/Poco这套自动化生态。iOS-Tagent在官方WDA基础上改了几件事调整了部分接口返回格式方便Airtest SDK直接解析。增加了对页面元素树快照的兼容处理尤其在H5和原生控件混搭场景下更稳定。修复了一些在iOS低版本上偶发的连接断开问题同时对iOS 17做针对性兼容。所以如果直接用官方WDA配合Airtest会遇到接口协议对不上的问题而直接用老版本的iOS-Tagent配合iOS 17又会遇到编译和运行时的兼容问题。这次升级的核心就是把iOS-Tagent的分支版本更新到与iOS 17匹配的版本重新编译部署再调整Airtest侧的连接配置和依赖版本让整条链路恢复正常。2. 动手前的准备清单环境、工具与签名在编译部署之前先把环境盘点清楚。我这次升级踩了不少环境上的坑提前准备好能省一整天时间。2.1 工具链版本要求iOS 17的真机调试和部署要求电脑上的Xcode版本不能太低。实测下来Xcode 14.3及以上版本才能正常识别iOS 17的设备并且包含对应版本的XCTest框架。旧版Xcode编译产物装到iOS 17上会出现设备不受支持或测试框架版本不匹配这类问题。另外两个刚需工具用于管理依赖的CarthageWDA工程里的WebDriverAgent.xcodeproj默认用Carthage来拉取部分第三方依赖。如果本机没有安装先brew install carthage装好。用于端口转发的工具把真机上的WDA端口映射到电脑本地。最常用的是libimobiledevice提供的iproxy也有团队用go-ios或tidevice。我个人习惯用tidevice因为它在iOS 17上都持续更新过后面会详细讲。然后是Python环境和Airtest框架。Airtest的版本建议升级到1.2.x以上的较新版本之前用过很老的1.1.x内置的WDA依赖对iOS 17支持不完整需要单独手动升级Airtest框架内的部分模块非常麻烦。2.2 证书签名的两种可行路线证书是部署WDA绕不开的一环iOS 17对签名的校验更严格了。根据你的账号情况有两种路线第一种使用普通Apple ID免费签名。优点是零成本缺点是生成的证书有效期只有7天每过一周就要重新签名部署一次适合个人学习或短期调试。第二种使用付费开发者账号创建Development证书。有效期通常是一年团队内部做持续集成也方便。如果你公司的自动化测试机是一台固定的Mac建议直接用第二种省去频繁重签的烦恼。无论用哪种都要注意在Xcode的Signing Capabilities面板里设置好对应的Team建议勾选“Automatically manage signing”让Xcode自动处理Provisioning Profile。如果手动签名配置文件过期或设备UUID未注册会在编译阶段报一堆莫名其妙的错误。注意iOS 17的真机需要开启开发者模式。在手机设置-隐私与安全里面拉到最底部打开开发者模式并重启手机。不开这个开关Xcode里能识别到设备但是跑测试的时候会一直卡在启动阶段。3. WDA在iOS 17真机上的完整部署流程3.1 拉取代码与依赖先确定你用的是哪个分支。iOS-Tagent的仓库默认主干分支一直在更新但并不是每个提交都针对iOS 17编译验证过。建议拉取最新的release标记版本或最近一次明确支持iOS 17的commit然后在本地切干净分支再编译。git clone https://github.com/your-project-address/iOS-Tagent.git cd iOS-Tagent git checkout tags/对应支持iOS17的版本号拉完代码后进入WebDriverAgent目录先执行Carthage更新cd WebDriverAgent carthage update --platform iOS这一步会拉取Carthage管理依赖到Carthage/Build/iOS目录。如果本机网络状况不好或者Carthage拉取超时可以在Cartfile里把依赖源临时切换为国内可访问的镜像源。另外carthage update和pod install不一样最终产物不需要CocoaPods的workspace方式打开直接open.xcodeproj即可。3.2 编译签名与安装用Xcode打开WebDriverAgent.xcodeproj在TARGETS列表里找到WebDriverAgentRunner选中它。这是主要在真机上安装的测试Runner。其他target如WebDriverAgentLib和WebDriverAgent一般是编译给模拟器或调试用的先不碰。然后做四步操作在General标签页里确认Bundle Identifier是自定义唯一值比如com.yourteam.WebDriverAgentRunner不要跟手机里已有App冲突。切到Signing Capabilities选择你的Team。把Deployment Target调低到兼容你当前测试设备的系统版本。实测iOS 17设备Deployment Target设为iOS 14.0是安全的既不影响编译也不影响运行。把设备连上Mac确保信任电脑并解锁屏幕Xcode的Devices窗口能看到这台iPhone。接着在Xcode顶部Scheme设置中把WebDriverAgentRunner的Scheme选好然后选择你的真机设备直接CmdR或Products - Build运行。注意CmdR会把Runner以XCTest方式安装并启动到真机上这是最方便的验证方式。首次运行的时候Xcode会提示正在安装测试Runner随后直接在Xcode里就能看到测试日志输出。如果一切正常手机桌面能看到一个名为WebDriverAgent的App图标虽然它其实是个测试宿主主要服务在后台跑着。3.3 启动WDA并验证连通性WDA运行起来之后它默认监听在真机上的8100端口。要从电脑访问需要做端口转发iproxy 8100 8100或者用我自己用着比较顺手的tidevicetidevice relay 8100 8100转发成功后在浏览器或终端里访问http://localhost:8100/status如果返回类似下面的JSON就说明WDA服务已经正常可用{ status: 0, value: { state: success, osVersion: 17.x, ios: { ip: ... } } }看到这个输出WDA部署这块就算成功了。如果这一步拿不到结果后面Airtest连接肯定也是空的所以务必停下确认好再往下走。4. Airtest连接iOS-Tagent的配置与脚本适配4.1 Airtest的iOS连接链路Airtest连接iOS设备走的是一条本地SDK - iOS-Tagent - XCTest链路。具体来说Airtest封装的iOS连接对象通过USB通道或Wi-Fi通道访问WDA暴露的HTTP接口然后用WebDriver协议发送各种自动化命令。在iOS 17适配过程中要先确认Airtest SDK自带的依赖是否更新。这一步最常见的问题是即便WDA已经跑通Airtest仍然报iOS设备连接失败或无法获取页面层级。原因通常是Airtest使用的iOS协议解析器和WDA的返回格式不完全兼容或者版本里缺少新设备型号的识别信息。升级建议pip install -U airtest pip install -U tidevicetidevice这个库在Airtest连接iOS的链路中是核心依赖相当于把USB通信和WDA暴露的服务串起来必须保证是最新版本。如果装完还出现连不上可以额外用tidevice list命令确认设备能不能被正常发现。4.2 配置参数与脚本写法在Airtest的connect_device中iOS连接的示例写法如下from airtest.core.api import * # 连接本地已转发端口的WDA dev connect_device(iOS:///127.0.0.1:8100)如果直接用USB连接不单独转端口也可以写成dev connect_device(iOS:///usb://?uuidxxxxxxport8100)这里uuid是指真机的Device UDID用tidevice list --json可以查到。新手朋友建议直接用第一种端口转发的方式先把链路跑通再考虑USB直连来减少转发环节的开销。脚本层面iOS 17之后有两个明显的适配变化第一touch和swipe的手势参数基本不用改但页面加载速度明显变慢元素未出现的概率更高。以前wait10秒能等到现在建议把auto_wait打开或者显式加wait(Template(...), timeout20)。第二Poco的UI Object定位原生控件没问题但如果是WebView/H5页面iOS 17上经常出现层级不全的情况。此时需要先切到WebView上下文再使用Poco的Web定位接口或者通过OCR方式识别文本坐标来点击。下面是一个简单的兼容写法示例from airtest.core.api import * from poco.drivers.unity3d import UnityPoco from airtest_air.air import Air auto_setup(__file__) # 连接并启动目标App start_app(com.example.someapp) sleep(2.0) # 方案一用Poco定位原生控件 poco UnityPoco(devicedev) if poco(com.example.button_login).exists(): poco(com.example.button_login).click() # 方案二遇到H5时使用OCR兜底 from airtest.core.cv import CV from airtest.air import OCR text_list OCR().readtext(CV().screen()) for item in text_list: if 登录 in item[0]: touch(item[1])这里用OCR兜底是源于实测中心得——iOS 17的WebView渲染层级变更后Poco经常拿不到Web控件结构OCR虽然性能一般但至少能保证脚本不在弹窗上停死。4.3 跑通第一个流程连接配置完成后先别急着写复杂流程用一个最小脚本验证整个链路。比如下面这段打开App、获取当前页面层级、点击一个按钮、断言下一个页面出现。from airtest.core.api import * from poco.drivers.unity3d import UnityPoco auto_setup(__file__) dev connect_device(iOS:///127.0.0.1:8100) start_app(com.example.someapp) poco UnityPoco(devicedev) # 等待首页按钮出现 poco(main_entry).wait_for_appearance(timeout15) poco(main_entry).click() # 断言跳转后的页面 assert poco(page_two_title).exists() print(flow pass)如果这个脚本能顺利跑完说明WDA的接口响应、iOS-Tagent的层级快照、Airtest的命令分发这一整条链路都已打通。接着再往里面添业务用例脚本试错成本就小多了。5. 实测下来的稳定性调优5.1 执行过程中的典型异常链路跑通不代表一切都稳了。iOS 17上跑了几天以后我发现下面三个问题特别值得关注元素快照超时或为空Poco获取页面元素时偶发返回空列表过几秒又恢复。Android上这个问题很少见iOS 17上却挺频繁尤其在页面转场动画期间获取层级几乎必然拿到空快照。截图黑屏或花屏有时screen()方法返回的图片是纯黑色。看起来像截图命令执行失败实际原因是WDA帮我们请求截图的时机和屏幕内容合成对不上。重新截一次就好但要避免在动画进行中触发截图。弹窗干扰iOS 17的定位/相册权限弹窗文案有变化如果旧脚本里匹配的是旧版弹窗文本就会卡住。这类问题在换新系统时几乎一定出现建议统一做一个关闭全部弹窗的公共步骤把权限选择全部点掉然后再走业务逻辑。5.2 三个提升稳定性的关键调整第一个调整在Poco获取元素前主动等待场景稳定。这里我写了一个自定义的重试函数def wait_and_click(poco, name, timeout15, interval1): start time.time() while time.time() - start timeout: try: if poco(name).exists(): poco(name).click() return True except: pass time.sleep(interval) raise TimeoutError(felement {name} not found)这个函数把存在性判断和点击放在同一轮循环里避免先wait_for_appearance再点击时元素因为页面刷新被重建导致点击失效。第二个调整统一设置Airtest的ST.FIND_TIMEOUT和ST.IMAGE_TIMEOUT放宽默认超时时间。在iOS 17上元素加载慢是一个普遍现象强制执行超时能减少脚本因为偶发延迟而中断的情况。第三个调整给手机屏幕设置永不锁屏同时关闭自动亮度调节、自动深色模式。实测自动深色模式在夜间会触发WDA的图层渲染切换导致截图颜色异常和元素识别失误关掉后稳定了很多。提示升级系统后自动化测试真机建议不要连接个人Apple ID账号也不开启iCloud同步。部分场景下系统弹窗Apple ID验证会突然出现并打断自动化流程测试机的系统偏好直接关掉所有Apple服务省心得多。6. 常见问题速查表这一节直接把最近半个月被问得最多、也最典型的几个问题整理成速查表大家遇到类似情况可以直接照着排查。阶段问题现象常见原因解决办法编译carthage update超时网络原因换镜像源或手动拷贝Carthage/Build/iOS编译CodeSign error: No signing certificate未配置Team/证书重新选择Team并确保证书存在安装提示Unable to install设备未信任电脑或未开启开发者模式解锁手机信任证书并重启打开开发者模式启动Runner启动闪退版本过老或代码分支不含iOS17适配切换到支持iOS17的代码版本重新编译启动/status返回404WDA还没完全启动等待5秒重试或看Xcode日志确认测试Runner是否正常连接Airtest报Connection refused端口转发失效重新执行iproxy或者tidevice relay并确认端口没被占用连接连接成功但获取层级为空页面动画未结束/Poco版本过旧在获取层级前强制等待并升级Airtest执行点击无效元素被弹窗遮挡加公共步骤先处理弹窗再点击执行截图黑屏动画中截图/深色模式避免动画过程中截图关闭深色模式如果你遇到的问题不在上表里有一个通用的排查路径先确认WDA本身是否正常访问/status如果WDA正常那问题一定出在Airtest和它之间的通信或协议解析上如果WDA都不正常就回到Xcode编译这一步重新验证签名和设备状态。通过这条路径大部分问题都能定位到具体层次。7. 最后聊聊我个人的体会这次iOS 17适配升级我最大的感受是iOS自动化环境的维护本质上是一个版本匹配工程不是每次都用最新版就万事大吉。你选择的Xcode版本、iOS-Tagent分支、Airtest依赖、签名证书、甚至手机系统设置都是互相牵制的。升级过程中最重要的一步是把每个环节的版本号记录下来形成一个固定组合。团队内如果有多台测试机最好统一用同一个组合避免出现我这台能跑你那台跑不了的情况。还有就是在没跑通最小验证脚本之前千万别直接上完整业务用例。先部署WDA、用/status确认服务正常、再跑通一次启动App-点击按钮-断言页面这三步只花十几分钟却能帮你把排查范围缩小很多。后面再遇到问题时至少能清楚知道故障是出在WDA还是Airtest。另外一个建议是给测试机专门准备一台不用于日常使用的备用iPhone。平台侧的系统大版本升级后旧的测试真机不要赶着升级而是让新系统版本和旧系统版本各保留一台设备这样既能验证系统兼容性又不会因为升级覆盖掉稳定的测试环境。等新系统方案跑稳了再把旧设备慢慢替换掉。最后再分享一个小技巧部署完WDA后可以在本地写一个一键启动脚本把启动Xcode编译-安装到真机-端口转发-检查/status这几步串起来。我团队里就是这么做的新来的同学不需要理解每一步的细节跑一遍脚本就能获得一个可用的iOS自动化环境。以后再做iOS升级适配时这套脚本也能帮我们快速试错省下大量重复劳动。
阅读完成 · 觉得有帮助?