WebDriverAgent配置实战:从零搭建iOS自动化测试环境 iOS自动化测试领域WebDriverAgent以下简称WDA是一个绕不开的名字。如果你做过移动端的UI自动化大概率听说过它或者正在为怎么把它跑起来发愁。这篇文章就聚焦一件事从零开始把一个可用的WebDriverAgent配置好让它稳定地跑在你的测试环境里并成功驱动真机执行自动化用例。这玩意儿是什么简单说WDA是运行在iOS设备上的一个服务程序。它通过响应WebDriver协议的REST API让你可以用外部的测试代码比如Python、Java去控制iOS应用——点击、滑动、输入、读取页面元素。它解决了什么问题以前做iOS自动化大家各搞各的工具碎片化严重而WDA凭借对WebDriver标准的支持把iOS自动化拉回到了一个相对统一的轨道上。适合谁看想搭建iOS自动化测试体系或者正在为Appium连不上真机而头疼的测试开发工程师。你不需要是iOS开发高手但最好对Xcode的基本操作和终端命令不陌生。1. 配置前的认知WDA到底在你的测试框架里扮演什么角色在动手敲命令之前先花两分钟搞清楚WDA的运行机制这能让后续排错时少走一半弯路。1.1 一句话说清WDA的工作流程WDA本质上是一个XCTest的测试工程。当你把它编译并安装到iOS设备上它会作为一个后台进程常驻。这个进程起来之后会开启一个HTTP服务默认端口是8100。你的测试代码发送HTTP请求给它WDA收到请求后调用苹果官方提供的XCTest框架的API去驱动模拟点击、滑动等操作。整个过程分为三层最上层你的测试脚本Python Appium或者Facebook的WebDriverAgent客户端都可以。中间层WDA服务负责把HTTP请求翻译成XCTest的调用。最底层iOS系统自身的XCTest框架真正执行UI操作。这个结构决定了你配置WDA的好坏直接影响了测试脚本执行的速度和稳定性。它不是可选项而是沟通测试脚本和iOS系统之间的翻译官。1.2 为什么是WDA而不是直接用XCTest工程很多刚接触的人会问既然WDA底层就是XCTest那我直接写XCTest用例不就行了行但问题在于XCTest用例是编译进App内的每次改动都需要重新编译、签名、安装非常重。而且它只能跑在同一个App进程里无法实现跨App的操作比如说从微信跳转到支付宝这种场景。WDA的出现就是为了解决这种重和封闭的问题。它把XCTest的能力变成了一套轻量的网络接口。一旦WDA编译安装完成你后续写测试用例、改测试逻辑都不需要再动iOS端的东西全部在外部代码层面完成。对于测试团队来说这意味着效率和灵活性大幅提升。所以你配置WDA本质上是在搭建一个稳定的、可持续复用的自动化测试基础设施而不只是过任务。2. 编译前的环境准备一五一十核对依赖项很多人配置WDA失败不是操作出错而是环境不匹配。这块我会按经验详细说明每一项都别跳过。2.1 核对开发环境型号与版本先看硬性要求一台Mac必须的。因为WDA的编译工具链Xcode只有macOS上才有。系统版本建议macOS 12以上太老的系统连新版本的Xcode都装不上更别提编译WDA了。Xcode版本非常重要。WDA是一个会随着iOS系统升级而需要同步更新的项目。如果你的iPhone系统是iOS 16那Xcode至少得是14以上。建议直接到App Store装最新稳定版。装完之后记得打开一次Xcode让它完成组件初始化否则命令行工具可能未生效。CarthageWDA老版本的依赖管理用的是Carthage新版虽然逐步迁移到了Swift Package Manager但Carthage在某些环节仍然需要用到装上稳妥。安装方式一行命令如果你安装了Homebrew在终端执行brew install carthageAppium可选但推荐虽然WDA是独立的但大多数人是通过Appium来使用它。Appium负责启动WDA、转发命令。后面连接部分我会详细讲。iOS设备 数据线必须是真机模拟器跑WDA意义不大因为模拟器的自动化直接用XCTest驱动更便捷。数据线建议用原装或MFi认证的线材质量差会导致设备频繁断连排查起来想砸键盘。提示每次升级iOS系统后WDA极大概率需要重新编译。因为你手机的权限校验和Xcode的开发者证书会发生变化。2.2 开发者证书与签名设置这是WDA配置中最容易卡住新手的一步。WDA要安装到你的真机上必须有合法的签名。Apple ID正常使用的话一个免费的Apple ID就行。但免费ID有7天有效期的限制——签名的证书7天后过期你需要重新编译安装WDA才能继续跑自动化。如果是公司项目或长期搭建测试平台建议用一个付费的开发者账号$99/年证书有效期一年省心得多。签名操作用Xcode打开WDA工程具体路径见下文然后在Signing Capabilities一栏选择你的开发者账号。这里要注意WDA工程里有多个target比如WebDriverAgentRunner是主target还有用于单元测试的必须把需要跑的那个target的签名都设置好。如果你只给主target签了名运行时还是会报错。我个人习惯是给项目里所有target统一换成同一个Team避免因为签名不一致导致的Failed to launch问题。2.3 获取WebDriverAgent源码WDA的源码托管在GitHub上但你需要的是其开源的代码仓库地址。可以通过如下方式获取git clone https://github.com/facebook/WebDriverAgent这个命令会把你拉取到的代码保存到当前目录并生成一个名为 WebDriverAgent 的文件夹。后续的编译工作都在这个目录里进行。3. 一步步实操编译、安装并启动WDA服务环境准备妥当之后我们进入核心实操环节。每一步我都会说明操作意图和可能踩到的坑。3.1 用Xcode直接跑通WDA最省事的路径这是我最常用的方式因为它能最直观地看到WDA是否在真机上跑起来了。用Xcode打开工程文件进入你下载的WebDriverAgent文件夹找到后缀名为.xcodeproj的工程文件双击它会默认用Xcode打开。设置签名按上一节说的在Xcode的Signing Capabilities里配置好你的Team。选择运行目标在Xcode顶部栏把目标设备从Any iOS Device切换成你的真机真机需要插在Mac上并解锁信任。点击Xcode左上角的Play按钮或者快捷键Cmd R开始编译运行。运行过程解析第一次编译会比较久因为要编译整个XCTest相关依赖。编译完成后Xcode会自动把WDA安装到你的真机上并启动它。怎么判断WDA是否启动成功注意看Xcode的Console输出如果出现了类似WebDriverAgentRunner start的日志或者“ServerURLHere”之类的信息基本就成了。此时WDA会在真机的8100端口开放服务。注意这种方式启动的WDA是需要手机和你的Mac连着数据线的。因为Xcode会在调试结束后尝试释放调试会话某些情况下WDA服务会被系统杀掉。所以更推荐的方式是让WDA独立在手机上运行不依赖Xcode的调试进程。这就用到了第二种方法。3.2 脱机启动WDA为什么和怎么做脱机指的是WDA安装到手机上之后不通过Xcode运行而是直接通过命令行唤起。这种方式稳定得多也适合集成到CI/CD流程里。编译好的App在哪当你通过Xcode Run成功一次之后其实已经有一个编译好的WDA产物通常叫做WebDriverAgentRunner-Runner.app生成了。它在磁盘上的位置会在你的工程配置里的Products目录下找到。你需要用到的是把这个app安装到手机上。通过命令行安装安装之前Xcode调试的会话必须释放掉。最简单的方法是拔掉数据线再插上然后在终端使用ios-deploy或直接ideviceinstaller这一类工具把跑出来的app装到手机上。这里我要强烈推荐使用go-ios它是一个开源工具安装简单功能齐全专门用来管理iOS设备。安装步骤brew install go-ios安装完成后用它来启动WDA核心命令是ios run --bundleid com.facebook.WebDriverAgentRunner.xctrunner解释一下这条命令的作用是告诉手机启动这个Bundle ID对应的App。com.facebook.WebDriverAgentRunner.xctrunner是WDA Runner的默认Bundle ID注意你在签名设置时这个Bundle ID可能会被Xcode自动改成你账号的唯一ID一般是你Team ID的前缀你需要去工程设置里确认实际的Bundle ID把它替换到命令中。执行完这条命令你应该能在终端看到日志输出WDA服务就在真机上跑起来了即使拔掉数据线服务也依然在。验证服务是否正常在Mac的终端里用Curl请求一下WDA的端口curl http://你的手机IP:8100/status如果返回一个包含value和build字段的JSON数据那就说明WDA服务启动成功并且网络是通的。3.3 端口转发让本机与真机建立通信真机的IP和你Mac的IP通常不在同一个网络段你在Mac上直接请求真机IP的8100端口大概率会不通。为了解决这个问题我们用端口转发。有两种方式方式一通过iproxy转发简单直接iproxy是随一些工具链附带的小工具。macOS上如果你装了libimobiledevice直接用brew install libimobiledevice iproxy 8100 8100这条命令的本意是把发到localhost:8100的请求转发到USB连接的真机的8100端口。执行成功后你在Mac上访问http://localhost:8100/status就能得到同样的JSON结果。这种方式不依赖Wi-Fi网络只要数据线在就很稳。方式二走局域网用于真机Wi-Fi连接如果你不想插线比如进行一些基于网络场景的测试需要确保手机和Mac连接的是同一个Wi-Fi。然后直接访问手机的IPhttp://手机IP:8100/status。这种方式会受网络波动影响稳定度相对较差。首选方案是什么在调试和日常开发阶段必须用方式一若需要跑一些长时间的任务并验证网络场景方式二更适合。但注意跑那个耗时长的并发任务还真不能随便拔线。4. 接入自动化测试框架Appium与WDA的联动配置WDA本身只是一条路我们要在上面跑车这车就是你的测试框架。主流的选择是Appium这部分把关键的配置说明白。4.1 配置Appium的WebDriverAgent所需Desired Capabilities在你的测试代码里启动一个iOS会话需要一个叫Desired Capabilities的参数集合。其中几个关键的字段怎么写{ platformName: iOS, platformVersion: 16.0, deviceName: iPhone 真机名, automationName: XCUITest, app: /path/to/your.app, wdaLocalPort: 8100, useNewWDA: true, udid: 你的设备UDID }automationName是XCUITestWDA就是基于XCUITest的。useNewWDA这个选项指的是每次Appium会重新安装WDA到手机上。建议设为false因为我们之前已经手动把WDA装好并启动了如果设为trueAppium会试图重新编译安装WDA如果Xcode签名和路径配置不一致很容易报错。自己手动装好然后让Appium直接连接已启动的WDA是最稳定的组合。wdaLocalPort是告诉Appium你希望它通过哪个本机端口去访问真机的WDA理解成端口转发的本机侧端口即可。4.2 处理Appium和WDA的版本兼容性Appium的版本众多不同版本对WDA的路径处理不太一样。Appium 1.x版本会在内部下载并维护一份WDA代码你的手动安装不影响它的内部逻辑Appium 2.x版本则更倾向于使用你指定的WDA位置。如果你用了useNewWDA: true且指定了自定义的WDA路径务必确认Appium有权限访问和编译它。我的建议是如果不做WDA源码二次开发就别折腾自定义路径直接用Appium内置的能力。4.3 用Python快速验证WDA是否响应指令环境搭好了怎么用最简单的用Appium的Python客户端发一个点击命令from appium import webdriver from appium.webdriver.common.appiumby import AppiumBy import time caps { platformName: iOS, platformVersion: 16.0, deviceName: iPhone, automationName: XCUITest, wdaLocalPort: 8100, useNewWDA: False, udid: 你的UDID, app: com.apple.Preferences # 举例系统设置 } driver webdriver.Remote(http://localhost:4723/wd/hub, caps) time.sleep(2) # 找一个元素并点击例如“通用”设置项 ele driver.find_element(AppiumBy.IOS_CLASS_CHAIN, **/XCUIElementTypeCell[label 通用]) ele.click() print(点击成功) driver.quit()如果这段脚本跑通了意味着你的WDA已经完全可用之后的自动化用例编写就可以放飞了。5. 容灾与排障常见问题排查手册这一节是我花最多时间整理的因为在实际使用中环境性问题远比代码问题多。5.1 编译失败类问题场景xcodebuild报错 No such module原因分析缺少依赖。老版本WDA需要通过Carthage管理依赖如果没有拉取依赖肯定编译不过。解决在工程目录下执行carthage bootstrap --platform iOS。注意这个命令执行时间较长耐心等待。新版本已使用Swift Package ManagerXcode打开工程时会自动解析依赖如果你的网络不佳可能解析失败。可以在Xcode的菜单 File Packages Resolve Package Versions 里手动重新拉取。场景编译时签名错误errSecInternalComponent原因分析钥匙串访问权限问题。解决打开钥匙串访问应用找到你的开发证书和对应的私钥双击设置为始终信任然后在Xcode的签名设置里重新选择一次Team先取消勾选再勾选触发一次证书刷新。5.2 启动与连接问题场景WDA能起来但是无法通过 USB 访问原因分析未设置端口转发或者手机未信任此电脑。解决先在手机的设置 - 开发者里确认开发者模式已开启且已信任。再执行iproxy 8100 8100然后访问http://localhost:8100/status测试。场景通过go-ios启动WDA时提示Failed to launch app原因分析常见于证书过期或者App被系统终止。解决最好的办法是重装一遍WDA用go-ios或 Xcode重新运行。如果重装还不能解决检查一下你的手机是不是存储满了。场景Appium连上WDA但执行用例时点击事件失灵原因分析这是一个经典坑。通常是因为你启动了多个WDA服务或者WDA与Appium内置状态不一致。解决关闭手机上所有其他WDA进程最近任务列表里向上滑掉或在终端执行 go-ios 的--terminate命令同时Appium里useNewWDA设为false只保留一个稳定的WDA实例运作。5.3 稳定性问题为什么跑着跑着WDA就退出了这是生产环境里出现频率最高的问题。表现是测试跑了半小时WDA服务突然无响应Appium报Timeout或Connection refused。原因1Xcode调试会话的干扰。如果你用Xcode Run的方式启动了WDA然后拔掉线Xcode会自动释放调试会话WDA就会被系统杀掉。解决必须用go-ios命令行方式启动WDA。原因2测试过程中操作频繁WDA进程内存占用过高。这是WDA的已知短板没有根本性解决方法。可以采用定时重启WDA的策略比如每跑满100条用例或30分钟就杀进程并重启WDA。在CI流水线里可以通过脚本控制。原因3调用driver.quit()后WDA还在后台运行下次连接失败。这种场景我在脚本里会显式执行一段shell代码连接前先通过go-ios重启一次WDA。5.4 键盘避坑经验在填写表单、搜索框输入时推荐用driver.hide_keyboard()或者直接调用WDA的sendKeys。注意一点iOS系统弹的键盘如果是中文输入法状态WDA的文本输入偶尔会串字或漏字。真实教训有一次我填一个搜索框输入自动化测试结果实际输入了自动化测 试多了个空格排查了半天。后来查资料发现这是输入法联想导致的。稳妥做法是在用例开始前强制将键盘切到英文输入法或者用WDA提供的clear接口先清空再输入。5.5 模拟器为什么连不上如果你在模拟器上尝试启动WDA大概率会失败。WDA的设计目标主要是真机。模拟器场景建议用XCTest直接跑测试没必要绕道WDA。强行在模拟器上跑WDA既慢又容易出幺蛾子纯属自找麻烦。6. 进一步扩展如何自定义WDA提升测试效率当你把WDA稳定跑起来之后如果只拿它来点击和滑动有点浪费。WDA其实还可以做更多。6.1 新增自定义API处理复杂手势WDA默认提供的接口有 tap、swipe、press等。但像Android端的scrollTo滑动到某个元素这种动作iOS端WDA支持得并不好。我在实际项目中尝试过给WDA增加一个自定义路由通过XCTest的XCUICoordinate方法来实现一个精确的坐标滑动协议。修改WDA源码后的重新编译并不复杂——在Xcode里打开工程改代码Run在手机上然后Appium里useNewWDA不需要改动直接连已启动的WDA即可。注意这要求团队成员有一定iOS开发能力。如果是纯测试背景的团队建议直接用driver.swipe()配合坐标处理不必动源码动一次源码就增加了维护成本。6.2 性能分析与远程调试WDA可以通过8099端口暴露一个TCP服务这个端口其实是为Remote Automation准备的。配合safari开发菜单的自动化选项你可以查看到当前WDA驱动的元素层级。在排查点击不生效这类问题时打开这个层级面板你能直观看到是不是有弹窗遮住了元素或者你的定位器是不是选错了层级。6.3 与Apple的XCUITest底层交互适当了解WDA的底层实现能让你在测试用例编写上更聪明。比如WDA内部通过XCUIApplication().windows等API枚举UI层级。当你的测试需要判断一个元素是否在当前屏幕上可见时不要用is_displayed()这种近似方法而应该用WDA提供的坐标和size来计算。经验之谈直接在Click前先判断元素坐标是否在屏幕坐标范围内可以减少很多点击失败。7. 实操心得总结这个配置过程我前前后后用过好几个版本掉进过不少坑。最后想掏心窝子说几句。关于证书如果你只是本地玩一玩免费账号够用了但7天续签一次确实麻烦。建议搭建测试平台的同学直接上付费开发者账号不要为这点成本浪费大量时间去反复配置。关于启动方式无论你用哪种框架强烈建议使用go-ios命令行方式独立拉起WDA让它不受Xcode会话管理。关于依赖管理编译WDA时确保Carthage和SwiftPM都正常工作网络不好的时候别心浮气躁这锅很多时候不全是代码的。还有个小技巧你可以把整个WDA配置过程写成一个Shell脚本固化进团队的CI流水线从检查环境、拉取源码、编译、签名、安装、启动、验证一气呵成。这样后续任何人接手机执行自动化不需要再手动折腾这套配置把曾经最耗时的环境搭建控制在十分钟以内。我第一次手动配时花了差不多一个下午接到自动流程之后才真正解放出来去关注用例本身的维护和稳定的问题。对于团队来讲这步沉淀的价值远高于一次性配置成功的成就感。