☰
iOS真机自动化测试:Appium+WDA从零部署到跑通第一个用例
2026/10/21 18:47:56 网站建设 项目流程

某团队当时要做 iOS 端的 UI 回归,第一反应就是 Appium + WDA 这套组合。团队里之前没人搭过真机测试环境,于是从 Xcode 版本匹配、签名证书、代码仓库选择到端口映射,把能踩的坑基本踩了一遍。这篇文章就把从零部署 WDA、让 Appium 连上真机、最终跑通第一条 iOS 自动化用例的过程完整记下来,包括那些让人血压立刻升高的报错和背后真正原因。适合刚开始接触 iOS 自动化测试、或者已经会用 Appium 但卡在 WDA 部署这一步的测试同学参考,尤其是要维护 Android 和 iOS 两套用例、又不想为 iOS 单独养一套代码仓库的团队。

1. 为什么 iPhone 真机测试首选 WDA 方案

1.1 iOS 自动化的两条路线

iOS 上的 UI 自动化大致有两条路。一条是苹果官方提供的 XCTest / XCUITest,直接用 Swift 或 Objective-C 写用例,通过 Xcode 内置的 Test Plan 跑。另一条就是 Appium 这类跨平台框架,把 WebDriver 协议翻译成 iOS 系统能理解的动作,其中承担翻译工作的关键组件就是 WDA。

这两条路线各有优势。官方 XCTest 和系统绑定最紧,遇到控件响应、权限弹窗这些问题时处理路径最短,但它有几个硬伤:测试代码必须写在 iOS 工程里,跑完一轮要归档、打包、签名,整个链路非常重;而且用例语言基本限定在 Apple 系,团队里写惯了 Java 或 Python 的人很难无缝接手。

Appium + WDA 的思路完全反过来。Appium 负责解析 WebDriver 指令,把“点击”“滑动”“查找元素”翻译成 JSON 请求,WDA 收到后再调用 XCTest 的机制去操作手机上的 App。这套方案最大的好处是测试工程和被测 App 完全解耦,一套 Python 用例既能在 Android 上跑,也能在 iOS 上跑,业务团队只需要补一套 XCUITest 驱动配置。而且 Appium 生态成熟,定位元素的方式、断言框架、报告系统都能直接复用。所以只要团队不是只做 iOS,Appium + WDA 基本是默认选择。

1.2 WDA 在整个链路里的角色

WDA 全称 WebDriverAgent,安装到 iPhone 之后,本质上是一个跑在手机上的 HTTP 服务。它内部通过 XCTest 的 API 去操作 App,对外暴露成一组 RESTful 接口。Appium 执行用例时,先把命令打包成 HTTP 请求发给 WDA,WDA 在手机上执行完点击、滑动、读取布局树这些动作,再把结果返回给 Appium。

这条链路看起来只有 Appium 和手机两层,但因为 WDA 本身是一个测试 Bundle,它必须被 Xcode 当作测试 Runner 安装到设备上。这就引入了三个麻烦:签名证书、系统版本兼容、USB 连接稳定性,都会直接影响 WDA 能不能起来。很多人部署半天跑不通,并不是 Appium 配得不对,而是 WDA 根本没能在手机上正常启动。

理解了这一层,排查问题时就可以按层次定位:命令发不出,先看 Appium 日志;WDA 起不来,看 Xcode 构建日志和系统日志;WDA 起来了但命令执行报错,再查元素定位和被测 App 状态。避免一上来就在 Appium 的 capability 里反复试。

2. 部署前最容易被忽略的版本与签名准备

2.1 工具链版本匹配是第一个暗坑

正式动手之前,我没先检查版本组合,结果耗了整整一个下午在编译错误里打转。iOS 自动化里最头疼的不是代码,而是 Xcode、WDA、Appium、手机系统这四者之间的兼容关系。

大致参考如下:

组件建议说明
Xcode当前主流新版本必须能覆盖目标设备 iOS 系统的 SDK
Appium2.x需要针对 xcuitest 驱动单独安装
WDA固定 release 或稳定 commit别天天跟着 master 跑
手机系统iOS 13 及以上WDA 对老系统支持有限,太旧的手机会编译失败

我见过最典型的问题,是拿老版本 WDA 跑 iOS 16 设备,编译时报一堆 API 过时错误。后来直接换成较新 release,问题消失。所以我的建议是:先用 Xcode 版本确定它能驱动的最高 iOS 系统,再反过来选 WDA 和 Appium 的版本。别让手机系统最新、Xcode 最旧、WDA 还是半年前的提交,这个组合基本必炸。

2.2 设备、证书和签名,一个都不能少

真机部署必须有开发者证书。免费 Apple ID 签名虽然能装,但签名有效期只有 7 天,测试环境隔几天就提示过期,非常痛苦。如果团队有条件,建议直接使用付费开发者账号,生成 iOS Development 类型证书,别用 Distribution 类型,后者用于发布,不能跑测试。

设备端还有几个容易忽略的点。手机连接 Mac 后,第一次会弹“信任此电脑”,不点信任的话后面什么都连不上。iOS 16 之后的系统需要在“设置—隐私与安全—开发者模式”里手动打开开发者模式,然后重启手机。锁屏也要处理,最好设置成永不锁屏,或者用自动化脚本定时唤亮,否则 WDA 跑一半屏幕黑了,后续用例全部失败。

签名相关动作记得在 Xcode 里先完成:登录账号,把设备 UDID 加到开发者账号的设备列表,然后在 WDA 工程的 target 里选好 Team。这个过程不做,后面用命令行构建时大概率报 “No profiles found” 或者 “Code Signing Error”。

2.3 先把依赖装全再开始

部署指令看着简单,缺依赖却会浪费大量时间。我的安装清单大致是:Homebrew、Node.js、Appium、Carthage、libimobiledevice、ios-deploy。

其中 Carthage 是 WDA 编译时需要拉取依赖的工具,新版 WDA 虽然逐渐向原生包管理迁移,但老版本仓库还是依赖它。libimobiledevice 提供idevice_id、idevicesyslog这些命令,后面排查设备连接问题非常有用。ios-deploy可以用于在命令行安装和启动测试包,但不是每个人都会用到,如果只依赖 Xcode GUI 操作,可以选装。建议把它们都装上,后续脚本可以直接调用。

装完先用appium-doctor检查一遍,它会提示 Node、Appium、常见驱动是否到位。注意appium-doctor只检查 Appium 自己的依赖,不检查 iOS 签名和 WDA 编译环境,所以即使它显示全绿,也不代表真机链路准备好了。

3. 从零部署 WDA 并让 Appium 连上真机的完整过程

3.1 拉取 WDA 源码并准备依赖

部署一开始就是拉代码。不建议直接从仓库 master 最新提交操作,宁可多花两分钟选版本。我的习惯是 clone 到本地之后,先看 tag 列表,找一个最近的 release,或者选一个已经验证过的 commit。如果团队内部有制品库,也可以直接使用别人编译好的 WDA 测试包。

git clone <你的代码仓库地址>/WebDriverAgent.git cd WebDriverAgent git checkout <选定的tag或commit> ./Scripts/bootstrap.sh

bootstrap.sh会调用 Carthage 拉取依赖,这一步对网络比较敏感,失败的话多跑几次基本能过。如果代码仓库访问有困难,也可以把 Carthage 源替换成团队内部的镜像,配置方法见 Carthage 官方说明。脚本跑完,检查WebDriverAgent.xcodeproj是否生成,这个工程文件就是后续编译和签名的基础。

3.2 配置签名和 Bundle ID

接着打开工程文件,选WebDriverAgentRunnertarget。先在 “Signing & Capabilities” 里把 Team 选成自己的证书团队,再把默认的 Bundle ID 改掉。

WDA 工程默认的 Bundle ID 需要换成自己公司的反向域名,比如com.yourcompany.webdriveragentrunner。这一步必须做,因为不同证书对 Bundle ID 有唯一性要求,和团队里已有 App 冲突时会直接安装失败。

这里有一个小技巧:命令行构建时,不要手工在 Xcode 里点运行,因为那样会把整个测试过程绑定在 GUI 上,后面自动化脚本没法复用。用以下命令来构建:

xcodebuild -project WebDriverAgent.xcodeproj \ -scheme WebDriverAgentRunner \ -destination 'id=<设备UDID>' \ -allowProvisioningUpdates build-for-testing

其中<设备UDID>替换成真机的 UDID。-allowProvisioningUpdates非常重要,它允许 Xcode 自动下载和更新 provisioning profile,避免手动去开发者后台配完再同步的繁琐过程。如果这一步报签名错误,回到 Xcode 的 target 设置里再次检查 Team。

3.3 启动 WDA 并验证状态接口

构建成功之后,接着用test-without-building把 WDA 跑到手机上。这个命令会安装并启动测试 Bundle,但不会重新编译。

xcodebuild -project WebDriverAgent.xcodeproj \ -scheme WebDriverAgentRunner \ -destination 'id=<设备UDID>' \ -allowProvisioningUpdates test-without-building

看到TEST SUCCEEDED之后,WDA 应该已经在手机上运行了。它默认监听 8100 端口,但这个端口是手机里的端口,Mac 上不能直接访问。用 libimobiledevice 自带的iproxy做端口映射,把手机 8100 映射到 Mac 本地:

iproxy 8100 8100

然后另开一个终端验证:

curl -X POST http://127.0.0.1:8100/status

如果返回 JSON 里能看到"ready": true,说明 WDA 已经正常起来。这个验证动作建议每次部署后都做一次,能快速区分是 WDA 没起来,还是 Appium 配置问题。

3.4 配置 Appium 连接已启动的 WDA

Appium 2.x 和 1.x 不一样,驱动需要单独安装。XCUITest 驱动是让 Appium 和 WDA 通话的插件,先安装:

appium driver install xcuitest

Appium 可以自己启动 WDA,但那样每次会话都会触发一次xcodebuild test-without-building,非常慢,而且容易受签名、端口冲突影响。我推荐让 Appium 去连我们已经在跑的实例。在 capability 里直接指定:

{ "platformName": "iOS", "automationName": "XCUITest", "udid": "<设备UDID>", "bundleId": "<被测App的BundleId>", "webDriverAgentUrl": "http://127.0.0.1:8100", "noReset": true }

把webDriverAgentUrl指到iproxy映射出来的本地端口,Appium 就不会再启动自己的 WDA。这样会话建立速度快很多,调试用例的时候体感非常明显。

4. 第一段自动化脚本与链路调试要点

4.1 关键 capability 逐个说明

刚配置 Appium 时,很容易被一堆能力参数弄晕。我挑最常用的几个说清楚:

配置项作用我的建议
platformName平台标识固定写 iOS
automationName驱动类型固定写 XCUITest
udid真机唯一标识用idevice_id -l或者 Xcode 窗口里复制
bundleId被测 App 的 Bundle ID必须准确,否则 WDA 找不到启动目标
webDriverAgentUrl指定已运行 WDA 地址调试阶段强烈建议使用
noReset是否保留 App 数据调试时写 true,避免每轮清数据
autoAcceptAlerts自动接受系统弹窗权限弹窗多的时候写 true

其中deviceName在实际使用中经常被忽略,XCUITest 驱动主要靠udid定位设备,deviceName更多是给报告看的,可以随便填或不填。

bundleId是最容易错的点。很多人填了 App 显示名称,结果 WDA 一直提示找不到应用。正确做法是在 Xcode 的 target 信息里复制,或者通过ideviceinstaller -l查看设备上已安装 App 的 Bundle ID。

4.2 用一段最小脚本验证点击和断言

环境通了之后,写一个最简单的 Python 用例来验证调用链路。下面这段脚本会启动一个演示 App,点击登录按钮,并断言页面上出现欢迎文本。

from appium import webdriver caps = { "platformName": "iOS", "automationName": "XCUITest", "udid": "<设备UDID>", "bundleId": "com.yourcompany.demo", "webDriverAgentUrl": "http://127.0.0.1:8100", "noReset": True, "autoAcceptAlerts": True, } driver = webdriver.Remote("http://127.0.0.1:4723/wd/hub", caps) driver.implicitly_wait(10) login_btn = driver.find_element("xpath", '//XCUIElementTypeButton[@name="登录"]') login_btn.click() welcome_label = driver.find_element( "xpath", '//XCUIElementTypeStaticText[@name="欢迎回来"]' ) assert welcome_label.is_displayed() driver.quit()

第一次跑这种脚本时,如果元素找不到,不要急着改 xpath,先打印driver.page_source,看一下 WDA 返回的布局树里控件的真实name和label。很多时候是元素名称和开发写的 accessibilityLabel 对不上。

4.3 怎样判断链路是通的

一段用例跑通后,不要以为就万事大吉。我的习惯是再做三轮验收:

第一轮,重启 Appium 服务,不重启 WDA,直接再跑一遍脚本,确认 Appium 真的在复用已有 WDA。第二轮,杀掉iproxy,重新映射端口,确认 WDA 状态接口依然可用。第三轮,在另一台设备上重复部署流程,确认脚本没有绑定某台机器的配置。

另外需要检查屏幕截图。Appium 会返回当前界面截图,人工看一眼,确认点击真的发生,而不是代码走了空逻辑。iOS 自动化里“用例绿了但界面没动”也是存在的,尤其当元素定位命中但点击被系统拦截时。

5. 实战中遇到次数最多的 6 个问题及解决记录

5.1 构建时报 Code Signing Error 或找不到 Profile

这是第一次部署最常碰到的问题。报错信息通常有两类:一类是No profiles for ... were found,另一类是Code signing is required for product type 'UI Testing Bundle'。

排查顺序很固定:先去 Xcode 的 Accounts 确认 Apple ID 已经登录;然后在 WebDriverAgentRunner target 的 Signing 里确认 Team 已经选择;最后看证书类型,必须是 Development 证书。构建命令里要带上-allowProvisioningUpdates,这能省掉很多手工同步描述文件的动作。

如果确认配置没问题但还是报错,试着在 Xcode 里把 target 切换到 WebDriverAgentRunner,然后 Product 菜单里 Clean Build Folder,清理完再走命令行。Xcode 的配置缓存有时候会让项目读取不到最新签名信息。

5.2 手机提示“未受信任的开发者”

这个弹窗常见于免费签名和新的证书。解决办法在手机上直接处理:打开系统设置里的设备管理,找到对应的开发者证书,手动点信任。点完之后会要求重新连接设备,重新跑一次xcodebuild启动 WDA。

付费证书一般不会出现这个提示,但如果是新注册的开发者账号,第一次在真机上跑测试包也会触发信任流程。所以部署前最好先把这个步骤走一遍,否则用例跑到一半手机弹窗拦截,非常影响判断。

5.3 WDA 启动后立即崩溃,日志一闪而过

WDA 启动后立刻退出,原因有很多,但最常见的是三类:端口被占用、证书过期、Bundle ID 和已有 App 冲突。

先查端口。很多工具会默认占用 8100,比如其他测试框架或调试脚本。杀掉占用进程,或者改 WDA 的监听端口,重新映射。再查证书。如果证书过期,手机上的 WDA 应用会直接闪退,重新登录开发者账号并刷新证书。最后检查 Bundle ID,确保改成了一个独有的标识。

想要看清崩溃原因,可以开另一个终端跑设备日志:

idevicesyslog -u <设备UDID> | grep WebDriverAgent

日志里会明确写出崩溃时调用栈和错误信息,比对着英语报错猜原因快得多。

5.4 Appium 报连接被拒绝,但 curl 状态是通的

curl http://127.0.0.1:8100/status返回正常,Appium 却报Unable to connect to WDA。这个矛盾通常不是端口问题,而是 Appium 尝试启动自己的 WDA,和当前正在运行的 WDA 冲突。

解决办法是确认 capability 里有没有webDriverAgentUrl,有就检查它是不是写成了手机上的 IP,或者写成了 localhost 但iproxy还没启动。如果没有指定webDriverAgentUrl,Appium 默认每次看wdaLocalPort,如果端口被iproxy占了,它可能认为自己起的 WDA 没起来。

我的习惯是调试阶段完全控制 WDA 生命周期:自己起 WDA,自己起iproxy,Appium 里只写webDriverAgentUrl,不写wdaLocalPort。这样端口冲突概率降到最低。还要确认 Mac 防火墙没有拦截本地回环地址,虽然少见,但确实遇到过。

5.5 模拟器上能跑,真机上一堆元素找不到

模拟器和真机的行为差异非常明显。真机上元素名称可能带上了缩略词,控件类型会随系统版本变化,滑动动画也会影响点击时机。比如模拟器里XCUIElementTypeButton的name是完整文案,真机可能只暴露了 accessibilityLabel。

首先要保证开发在代码里设置了accessibilityIdentifier或accessibilityLabel,没有标识就有无从下手。其次,定位时多用相对路径,比如从列表项内部找子元素,而不是写全局唯一 xpath,这样对真机上的动态布局更健壮。

另外,真机上系统弹窗出现时机不稳定。autoAcceptAlerts虽然能自动点掉系统弹窗,但如果 App 自己实现了自定义弹窗,还是要写专门的用例处理。不然元素在屏幕上却不可点,断言永远失败。

5.6 老系统设备跑不了新版 WDA

团队里总有几台 iOS 系统版本特别旧的设备,比如发布会上送测的样机长期不升级。新版 Xcode 的 SDK 一旦不兼容老系统,WDA 编译直接报架构或 API 错误。

这种情况只有一个靠谱思路:按照设备系统版本倒推 WDA 版本。在 WDA 仓库里找到支持该 iOS 系统的 release,把代码切到那个版本,再用对应版本的 Xcode 去构建。不要指望一份 WDA 源码通吃所有系统,尤其是在同时维护 iOS 15、iOS 16、iOS 17 的团队里,最省事的做法是给每个系统版本保留一份已经编译好的 WDA 测试包。

下表是我常用的排查路径:

问题第一反应验证方式
构建失败检查签名和 Team看 Xcode 的 Signing 面板
启动崩溃看系统日志idevicesyslog
连接被拒检查 iproxy 和端口curl状态接口
元素找不到打印 page_sourcedriver.page_source
老系统不兼容换旧版 WDA查看 release 对应系统

6. 让 WDA 更省心的四个实操习惯

6.1 不要用热插拔方式连设备

USB 线频繁热插拔很容易让 WDA 进入半死状态。尤其是测试过程中拔掉再插回去,手机端的 WDA 不会自动恢复,Mac 上的iproxy也会断掉。我的做法是专门准备一根短一点的物理线,固定在一个 USB 口上,竖着放手机,减少触碰。需要换设备时,先杀iproxy,拔线,再插新设备,重新跑一次 WDA 构建和端口映射。虽然多花一两分钟,但比跑着跑着突然无响应要划算。

6.2 固定 WDA 版本而不是天天跟最新提交

WDA 仓库更新频繁,某个提交改动一下元素策略,第二天用例可能全部变红。所以接到新项目时,一定要把 WDA 版本锁住。团队内部最好把WebDriverAgent源码包连同编译好的测试包一起存档,新人来了直接拿存档跑,不需要重现网络依赖和签名过程。

锁版本的操作很简单,在拉代码时记下当前 tag 或 commit hash,之后都用这个值:

git checkout <tag或commit>

如果团队有多个 iOS 系统版本,就按系统版本维护多个存档,每个存档里写清楚对应的 Xcode 和 Appium 版本。

6.3 日志单独留目录,排查才有依据

WDA 部署过程中最怕的现象是“不知道什么时候开始失败的”。我养成的习惯是第一次部署就规划好日志目录,专门存放三类日志:Appium 运行日志、xcodebuild 构建日志、设备系统日志。启动脚本里重定向输出,并带时间戳,方便回溯。

nohup appium --log-level info --log-file ./logs/appium_$(date +%Y%m%d_%H%M%S).log & xcodebuild ... 2>&1 | tee ./logs/build_$(date +%Y%m%d_%H%M%S).log

把日志留好之后,再遇到连接失败,先看构建日志里有没有签名错误,再看设备日志里有没有 XCTest 崩溃,直接缩小问题范围。省去了“重新跑一遍复现”的流程。

6.4 批量自动化部署时用脚本封装备份

为整个流程写一个初始化脚本,能极大降低团队协作成本。脚本至少包含三步:检查设备 UDID、启动iproxy端口映射、调用xcodebuild test-without-building拉起 WDA。Appium 不用在脚本里启动,通常交给执行机上的服务管理器统一管理。

如果经常要切换多台设备,建议把设备 UDID 作为脚本参数传入,并维护一个设备列表文件,配套记录每台设备对应的 WDA 端口。这样新设备接入时,只要改设备列表,不用改脚本本身。

最后分享一点实际操作体会

第一次部署 WDA 时我花了两天,后来发现 90% 的时间都耗在版本匹配和签名上。等这些流程稳定下来,真正帮我提升效率的其实是一个很小的习惯:每次拿到新设备,固定走一遍“查系统版本、查 Xcode 兼容性、编译签名、验证 status 接口”的流程,并把结果记录在团队文档里。

如果你现在正在被 WDA 部署折磨,建议先从版本组合查起,别急着看 Appium 的日志。只要 WDA 能在手机上起来,剩下的事情基本都是时间问题。另外,写第一套用例时,一定要拉着开发把 accessibilityLabel 补上,否则后面所有定位都会像在迷宫里探路。这条经验比任何脚本配置都值钱。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询