iOS自动化测试实战:WebDriverAgent与Appium架构解析与配置指南
1. 项目概述:为什么我们需要WebDriver-Agent-Appium?
如果你是一名iOS开发者或者测试工程师,那么你一定对“自动化测试”这个词不陌生。在追求快速迭代和高质量交付的今天,手动一遍遍点击App来回归测试,不仅效率低下,而且容易出错,尤其是在面对几十上百个测试用例时。这时候,一个稳定、高效的自动化测试框架就成了团队的“救命稻草”。而当我们把目光聚焦在iOS平台时,WebDriver-Agent-Appium这个组合就成为了绕不开的核心技术栈。
简单来说,这是一个“三明治”结构。最底层是苹果官方提供的WebDriverAgent(简称WDA),它是一个由Facebook维护(后来由Appium社区接手)的iOS移动UI自动化测试服务器。你可以把它理解为一个安装在iPhone或iPad上的“遥控器”,它允许外部指令通过HTTP协议来操控你的设备,比如点击屏幕上的某个按钮、输入文字、滑动页面等。中间层是Appium,它是一个跨平台的、开源的移动端自动化测试框架。Appium本身并不直接控制设备,它更像一个“翻译官”和“调度中心”,它接收我们用Python、Java、JavaScript等语言编写的测试脚本,然后将这些指令“翻译”成WDA能够理解的协议,并发送给在设备上运行的WDA服务。最终,由WDA来执行具体的UI操作。
所以,当我们谈论“WebDriver-Agent-Appium”时,我们实际上是在谈论一套完整的、基于客户端-服务器架构的iOS UI自动化测试解决方案。它解决了原生iOS自动化(如XCUITest)必须依赖Xcode和Mac环境、脚本语言受限(主要是Swift/Objective-C)的问题,让开发者可以用自己熟悉的编程语言,在任何操作系统(Windows, Mac, Linux)上编写测试脚本,远程控制真实的iOS设备或模拟器。这对于需要持续集成(CI/CD)、大规模兼容性测试或者团队技术栈不统一的场景来说,价值巨大。
2. 核心架构与工作原理深度拆解
要玩转这套工具,不能只停留在“会用”的层面,理解其内部的工作原理,才能在遇到问题时快速定位,甚至进行定制化开发。让我们把这个“三明治”一层层剥开来看。
2.1 WebDriverAgent:设备端的“执行引擎”
WDA是整个体系的基石。它本身是一个用Objective-C编写的iOS应用(一个.xctest测试包)。当你把它安装到你的iOS设备(无论是真机还是模拟器)上并启动后,它会在设备上开启一个HTTP服务器。
它的核心工作流程如下:
- 启动与监听:WDA启动后,会在设备的某个端口(默认8100)上启动一个WebSocket服务器。这个服务器遵循WebDriver协议,这是一种用于远程控制Web浏览器的标准协议,Appium将其扩展用于移动端。
- 映射UI树:WDA利用苹果提供的XCUITest框架私有API,获取当前前台应用的整个UI层次结构。这个结构是一个树状模型,包含了每一个UI元素(如按钮、文本框)的详细信息,包括类型、名称、坐标、是否可点击等属性。这个过程通常被称为“dump source”或“获取页面源”。
- 接收与解析指令:Appium服务器通过HTTP请求将操作指令(例如:
/session/{sessionId}/element查找元素,/session/{sessionId}/element/{elementId}/click点击元素)发送到WDA的WebSocket端点。 - 执行与反馈:WDA接收到指令后,在其内部调用对应的XCUITest API来执行真实的UI操作。操作完成后,它会将结果(成功或失败,以及可能的返回值)封装成HTTP响应,返回给Appium服务器。
注意:由于WDA使用了XCUITest的私有API,因此它的能力与苹果官方的UI测试框架基本一致,但也受其限制。例如,它无法直接控制系统级别的弹窗(如网络权限请求),也无法操作非当前App的界面。对于系统弹窗,通常需要结合其他工具或方法处理。
2.2 Appium:跨平台的“协议翻译与调度中心”
Appium的设计哲学非常巧妙,它提出了一个核心概念:“你不需要为了测试而重新编译你的应用或修改它”。这是通过利用各个平台现有的自动化框架来实现的。对于iOS,这个框架就是XCUITest(通过WDA代理)。
Appium服务器的关键角色:
- 会话管理:当你的测试脚本(Client)启动一个测试时,它会向Appium服务器发送一个包含
desired capabilities(期望能力)的请求,例如指定平台iOS、设备名、App路径等。Appium服务器根据这些信息,创建一个唯一的session,并负责启动对应的WDA服务(或连接到已启动的WDA)。 - 协议桥接:Appium定义了一套统一的JSON Wire Protocol。你的测试脚本无论用哪种语言编写,都通过这套协议与Appium服务器通信。Appium服务器则将这套通用协议“翻译”成目标平台自动化框架(对于iOS就是WDA的WebDriver协议)能理解的指令。
- 驱动管理:Appium通过“驱动程序(Driver)”来支持不同平台。对于iOS,就是
XCUITest Driver。这个驱动包含了所有iOS平台特有的逻辑,比如如何启动WDA、如何处理iOS特有的定位策略、如何管理应用生命周期等。
一个完整的交互链条示例:你的Python脚本driver.find_element_by_accessibility_id(“登录”).click()会经历以下步骤:
- Python客户端库将指令封装成HTTP请求发送给Appium服务器(默认端口4723)。
- Appium服务器的XCUITest驱动收到请求,将其转换为WDA协议格式的请求。
- Appium服务器将这个请求转发给设备上WDA服务运行的端口(如8100)。
- WDA接收到请求,通过XCUITest找到
accessibility_id为“登录”的元素,并执行点击操作。 - WDA将点击成功的结果返回给Appium服务器。
- Appium服务器再将成功结果返回给你的Python脚本。
2.3 真机与模拟器的差异处理
这是配置过程中最容易踩坑的地方。虽然原理相同,但针对模拟器和真机,WDA的安装、启动和连接方式有显著区别。
模拟器:
- 安装:最简单。Appium(通过
appium-xcuitest-driver)在创建会话时,会自动将编译好的WDA Runner安装到指定的模拟器中。你几乎不需要手动干预。 - 签名:模拟器环境对应用签名要求非常宽松,通常使用Xcode提供的自动管理签名或开发证书即可。
- 启动:Appium会自动启动WDA进程。
- 优势:速度快,环境纯净,适合快速开发和调试测试脚本。
真机:
- 安装:相对复杂。需要先将WDA项目源码下载到本地,用Xcode打开,配置你的Apple开发者账号(Team ID)和Bundle Identifier,然后选择你的真机设备进行编译和安装。这个过程涉及代码签名,是最大的障碍。
- 签名:必须使用有效的Apple开发者证书(付费账号)或免费的个人开发证书(需在Xcode中登录Apple ID,且设备需信任该证书)。签名配置错误是导致WDA在真机上启动失败的最常见原因。
- 启动:安装后,你需要在设备上手动信任开发者证书,然后通过Xcode运行WDA,或者使用
xcodebuild命令启动。在Appium中,可以通过配置webDriverAgentUrl直接连接到已手动启动的WDA服务,以绕过复杂的自动启动流程。 - 优势:能反映真实用户环境,测试性能、网络、传感器(如GPS、陀螺仪)等更准确。
实操心得:我强烈建议测试脚本的开发与调试阶段在模拟器上进行,因为环境搭建简单,重置方便。等到脚本逻辑稳定后,再配置真机环境进行最终的兼容性与性能验证。这样可以极大提升效率,避免在脚本逻辑和环境问题之间纠缠不清。
3. 环境搭建与核心配置实战指南
理论讲完了,我们动手搭一个。这里我以macOS环境为例,因为iOS开发与测试离不开Xcode。目标是搭建一个能同时在iOS模拟器和真机上运行自动化测试的环境。
3.1 基础环境准备
- 安装Xcode:从Mac App Store安装最新稳定版的Xcode。安装后,务必打开一次,同意用户协议,并安装额外的命令行工具(
xcode-select --install)。 - 安装Homebrew:macOS的包管理器,用于安装其他依赖。
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" - 安装Node.js与npm:Appium服务器是基于Node.js的。
安装后,验证版本:brew install nodenode -v(建议版本14+),npm -v。
3.2 安装与配置Appium
有两种主要方式:全局安装和通过@appium工具安装。推荐后者,它能更好地管理多个Appium版本。
安装Appium命令行工具:
npm install -g appium安装后,你可以通过
appium -v检查版本,并通过appium命令启动服务器。安装Appium驱动程序:Appium 2.0之后,驱动需要单独安装。
npm install -g appium-driver-xcuitest这个
xcuitest驱动就是专门用于iOS的。安装Appium客户端库(以Python为例):在你的测试项目目录中,安装Python客户端。
pip install Appium-Python-Client
3.3 配置WebDriverAgent(针对真机)
这是最关键的步骤。我们将手动编译并安装WDA到真机,以便Appium连接。
获取WDA源码:
git clone https://github.com/appium/WebDriverAgent.git cd WebDriverAgent使用脚本安装依赖:
./Scripts/bootstrap.sh这个脚本会安装必要的Carthage依赖。
用Xcode打开项目:
open WebDriverAgent.xcodeproj配置签名(最关键的一步):
- 在Xcode顶部的Scheme选择器处,确保选中
WebDriverAgentRunner-> 你的真机设备(不是模拟器)。 - 在项目导航区选中
WebDriverAgent项目,然后选中WebDriverAgentRunnerTarget。 - 进入Signing & Capabilities标签页。
- 取消勾选Automatically manage signing。
- 在Provisioning Profile处,选择一个你开发者账号下的配置文件(Provisioning Profile)。如果没有,你需要回到Automatically manage signing,让Xcode自动生成一个(需要登录Apple ID)。
- 确保Bundle Identifier是唯一的,通常需要修改默认的
com.facebook.WebDriverAgentRunner,比如改成com.yourname.WebDriverAgentRunner。 - 同样地,检查
WebDriverAgentLibTarget的签名配置,确保一致。
- 在Xcode顶部的Scheme选择器处,确保选中
编译与运行:
- 在Xcode中,按下
Cmd + R运行。首次运行会在真机上安装WebDriverAgentRunner应用。 - 安装后,你需要到手机的设置 -> 通用 -> VPN与设备管理中,信任你的开发者证书。
- 再次在Xcode中运行。如果成功,你会在Xcode控制台看到一大串日志,其中包含关键信息:
ServerURLHere->http://[设备IP]:8100<-ServerURLHere。记下这个IP和端口(通常是8100)。
- 在Xcode中,按下
验证WDA服务:
- 确保你的手机和电脑在同一个Wi-Fi网络下。
- 在电脑浏览器中访问
http://[设备IP]:8100/status。如果返回一个JSON,包含"value"和"sessionId"等信息,说明WDA服务运行成功。 - 访问
http://[设备IP]:8100/inspector,你可以看到一个简陋的UI查看器,能显示当前设备的屏幕和UI树,这是一个非常有用的调试工具。
重要提示:真机测试时,WDA的IP地址可能会因为Wi-Fi重连而变化。一种更稳定的方法是使用
iproxy工具将设备的端口映射到本地。首先通过USB连接设备,然后:brew install libimobiledevice, 接着运行iproxy 8100 8100。这样,你就可以通过访问http://localhost:8100来连接WDA了。Appium也支持通过webDriverAgentUrl配置直接连接这个本地地址。
3.4 编写你的第一个测试脚本
环境就绪,我们来写一个简单的Python脚本,在模拟器上打开计算器App并点击一个数字。
from appium import webdriver from appium.webdriver.common.appiumby import AppiumBy import time # 定义Desired Capabilities,这是告诉Appium你要测试什么设备、什么App的核心配置字典。 desired_caps = { 'platformName': 'iOS', 'platformVersion': '17.2', # 改为你的模拟器系统版本 'deviceName': 'iPhone 15 Pro', # 改为你的模拟器名称 'automationName': 'XCUITest', # 指定使用XCUITest驱动 'app': 'com.apple.calculator', # 系统计算器的Bundle ID # 如果测试自己的App,则使用 ‘app’: ‘/path/to/your/app.app’ 'noReset': True, # 不重置App状态 'wdaStartupRetries': 4, 'wdaStartupRetryInterval': 20000, } # 连接Appium服务器(假设运行在本地默认端口4723) driver = webdriver.Remote('http://localhost:4723', desired_caps) try: # 等待App启动 time.sleep(2) # 使用accessibility id定位数字按钮“7”。在计算器App中,每个按钮都有对应的accessibility identifier。 # 如何获取?可以用Appium Desktop Inspector或Xcode的Accessibility Inspector。 number_seven = driver.find_element(AppiumBy.ACCESSIBILITY_ID, "7") number_seven.click() print("成功点击数字7") # 再点击加号 plus_button = driver.find_element(AppiumBy.ACCESSIBILITY_ID, "add") plus_button.click() print("成功点击加号") # 可以继续其他操作... time.sleep(2) except Exception as e: print(f"测试过程中发生错误:{e}") # 可以在这里截图 driver.save_screenshot('./error_screenshot.png') finally: # 无论如何,最后都要退出驱动,关闭会话 driver.quit() print("测试结束,会话已关闭")脚本解析与注意事项:
desired_caps:这是与Appium服务器建立会话的“合同”。每个参数都至关重要。automationName: XCUITest是必须的,告诉Appium使用iOS驱动。- 定位策略:
AppiumBy.ACCESSIBILITY_ID是iOS上最稳定、首选的定位方式。它对应的是UI元素的accessibilityIdentifier属性,需要开发同学在编码时设置。如果无法获取,次选方案是AppiumBy.CLASS_NAME(如XCUIElementTypeButton)结合其他属性。 - Appium服务器:运行脚本前,务必在终端先启动Appium服务器:
appium。或者使用appium --address 127.0.0.1 --port 4723指定地址和端口。 - 模拟器:确保你指定的模拟器(
deviceName和platformVersion)已经在Xcode的Window -> Devices and Simulators中创建并可用。
4. 元素定位策略与高级交互技巧
元素定位是UI自动化的灵魂。定位不到元素,一切操作都无从谈起。在iOS的XCUITest框架下,我们主要有以下几种定位器(Locator Strategy)。
4.1 核心定位策略详解
accessibility id (首选):
- 原理:对应UI元素的
accessibilityIdentifier属性。这是专门为自动化测试设计的属性,与用户看到的文本(label)无关,最稳定。 - 使用:
driver.find_element(AppiumBy.ACCESSIBILITY_ID, “myButton”) - 如何获取:要求开发设置,或使用Appium Inspector、Xcode Accessibility Inspector查看。
- 原理:对应UI元素的
class name:
- 原理:对应UI元素的类型,如
XCUIElementTypeButton,XCUIElementTypeStaticText,XCUIElementTypeTextField。 - 使用:通常需要结合其他条件(如
xpath)来精确定位,因为同一页面同类元素太多。 - 示例:
driver.find_elements(AppiumBy.CLASS_NAME, “XCUIElementTypeButton”)[0](获取第一个按钮)。
- 原理:对应UI元素的类型,如
xpath (强大但需谨慎):
- 原理:使用XML路径语言来定位元素。功能最强大,可以表达复杂的层级关系,但执行速度相对较慢,且容易因UI结构微小变动而失效。
- 使用:
driver.find_element(AppiumBy.XPATH, ‘//XCUIElementTypeButton[@name=“登录”]’) - 技巧:尽量避免使用绝对路径(以
/开头),多使用相对路径和属性组合。在iOS中,@name属性通常对应accessibilityLabel。
predicate string (iOS特色,推荐):
- 原理:使用NSPredicate格式的字符串进行定位。这是iOS原生支持的一种非常灵活和高效的查询方式。
- 使用:
driver.find_element(AppiumBy.IOS_PREDICATE, ‘label == “用户名” AND enabled == true’) - 常用表达式:
label == “...”:匹配accessibilityLabel。value == “...”:匹配当前值(如输入框文本)。name == “...”:匹配accessibilityIdentifier(即accessibility id)。type == “XCUIElementTypeButton”:匹配元素类型。- 支持
AND,OR,NOT,BEGINSWITH,CONTAINS,ENDSWITH等操作符。
class chain (iOS特色,性能优于xpath):
- 原理:类似xpath,但是是苹果为XCUITest优化的一种查询语言,性能比xpath好。
- 使用:
driver.find_element(AppiumBy.IOS_CLASS_CHAIN, ‘**/XCUIElementTypeButton[name == “提交“]’)
定位策略选择优先级建议:accessibility id>iOS predicate string>iOS class chain>xpath。尽可能让开发同学为可交互元素添加唯一的accessibilityIdentifier,这是打造稳定测试套件的基石。
4.2 等待机制:让脚本更健壮
UI渲染需要时间,网络请求需要时间。直接定位元素很可能因为页面未加载完成而失败。因此,显式等待是必须的。
from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from appium.webdriver.common.appiumby import AppiumBy # 设置一个最长等待10秒的等待器 wait = WebDriverWait(driver, 10) # 等待直到某个元素出现 login_button = wait.until( EC.presence_of_element_located((AppiumBy.ACCESSIBILITY_ID, “登录”)) ) login_button.click() # 等待直到某个元素可点击 submit_button = wait.until( EC.element_to_be_clickable((AppiumBy.IOS_PREDICATE, ‘name == “submitButton”’)) ) submit_button.click()实操心得:不要滥用time.sleep()这种固定等待,它会让测试变慢且不可靠。始终优先使用显式等待(WebDriverWait)。对于整个页面的加载,可以等待一个关键元素(如首页Logo)的出现作为页面加载完成的标志。
4.3 高级交互:滑动、长按、多点触控
Appium通过TouchAction和W3C ActionsAPI支持复杂手势。现在更推荐使用W3C Actions。
from appium.webdriver.common.touch_action import TouchAction import time # 示例:从屏幕底部向上滑动(模拟上拉手势) action = TouchAction(driver) start_x = driver.get_window_size()[‘width’] * 0.5 # 屏幕中心X start_y = driver.get_window_size()[‘height’] * 0.8 # 屏幕底部附近Y end_y = driver.get_window_size()[‘height’] * 0.2 # 屏幕顶部附近Y action.press(x=start_x, y=start_y).wait(500).move_to(x=start_x, y=end_y).release().perform() # 示例:长按某个元素 element = driver.find_element(AppiumBy.ACCESSIBILITY_ID, “某个可长按项”) action.long_press(element, duration=2000).release().perform() # 长按2秒 # 示例:使用W3C Actions进行滑动(更现代的方式) from selenium.webdriver.common.action_chains import ActionChains from selenium.webdriver.common.actions import interaction from selenium.webdriver.common.actions.action_builder import ActionBuilder from selenium.webdriver.common.actions.pointer_input import PointerInput # 创建一个触摸指针 finger = PointerInput(interaction.POINTER_TOUCH, “touch”) actions = ActionBuilder(driver, mouse=finger) # 按下、移动、释放 actions.pointer_action.move_to_location(start_x, start_y).pointer_down().pause(0.5).move_to_location(start_x, end_y).pointer_up() actions.perform()5. 常见问题排查与性能优化实录
即使环境搭建成功,脚本编写过程中也会遇到各种“坑”。这里我记录了一些最常见的问题和解决方法。
5.1 连接与启动类问题
| 问题现象 | 可能原因 | 排查步骤与解决方案 |
|---|---|---|
| Appium服务器启动失败,端口被占用 | 端口4723已被其他进程占用 | lsof -i :4723查看占用进程的PID,kill -9 <PID>结束它。或启动Appium时指定其他端口:appium -p 4724 |
创建会话失败,报错Could not find a driver for... | 未安装对应的Appium驱动 | 运行appium driver list查看已安装驱动。使用appium driver install xcuitest安装。 |
| 真机测试时,Appium无法启动WDA,日志显示签名错误 | WDA项目代码签名配置错误 | 1. 检查Xcode中WebDriverAgentRunnerTarget的Bundle Identifier是否唯一,签名证书和配置文件是否正确。2. 尝试完全关闭Xcode,删除 ~/Library/Developer/Xcode/DerivedData/下WebDriverAgent相关的文件夹,重新打开项目编译。3. 在手机设置中彻底删除之前安装的 WebDriverAgentRunner应用,重新安装。 |
模拟器启动应用失败,报错bundleId does not exist | desired_caps中的app路径或Bundle ID错误 | 1. 对于模拟器上的.app包,使用绝对路径,如/Users/name/Projects/MyApp/build/Release-iphonesimulator/MyApp.app。2. 对于系统应用或已安装应用,使用正确的Bundle ID,可通过 ideviceinstaller -l(真机)或查询文档获取。 |
| 脚本执行缓慢,每个操作间隔很久 | 默认的newCommandTimeout或隐式等待设置过长;或者使用了time.sleep | 1. 检查desired_caps中是否设置了过大的newCommandTimeout(默认60秒)。2. 避免使用隐式等待 driver.implicitly_wait(10),改用显式等待。3. 移除不必要的 time.sleep。 |
5.2 元素定位与交互类问题
| 问题现象 | 可能原因 | 排查步骤与解决方案 |
|---|---|---|
| 找不到元素(NoSuchElementException) | 1. 定位器写错了。 2. 页面尚未加载完成。 3. 元素在WebView或混合应用中。 | 1.使用Appium Inspector实时调试:启动Inspector会话,查看当前页面的UI树,确认元素的准确属性。 2.添加显式等待:在定位前等待元素出现或可交互。 3.切换上下文:如果是WebView,需要使用 driver.contexts获取所有上下文,并切换到WebView上下文(如WEBVIEW_com.xxx.xxx)后再定位。 |
| 元素定位到了,但点击无效 | 1. 元素不可点击(enabled=false)。 2. 被其他元素遮挡。 3. 坐标点击有偏差。 | 1. 使用element_to_be_clickable条件进行等待。2. 尝试使用 driver.execute_script(‘mobile: tap’, {‘x’: x, ‘y’: y})进行坐标点击。3. 检查是否有弹窗、键盘遮挡。 |
| 在列表(如TableView)中滑动查找元素失败 | 滑动距离/速度不合适,未触发列表滚动。 | 使用Appium提供的专用滚动查找方法,这比手动计算滑动更可靠:driver.execute_script(‘mobile: scroll’, {‘direction’: ‘down’})或使用 mobile: swipe手势。 |
| 输入文本时,特别是中文,出现乱码或失败 | 键盘未正确弹出或输入法问题。 | 1. 点击输入框后,先clear()再send_keys()。2. 对于复杂情况,可以尝试使用 driver.set_value(element, ‘text’)。3. 在 desired_caps中设置unicodeKeyboard: True和resetKeyboard: True,使用Appium的自带键盘(但可能无法输入中文)。 |
5.3 性能优化与最佳实践
使用
driver.quit()而非driver.close():quit()会销毁整个会话,释放所有资源;close()只是关闭当前窗口,在移动端可能行为不一致。务必在finally块中调用quit()。合理管理会话生命周期:对于一组相关的测试用例,尽量复用同一个
driver会话,而不是每个用例都重新启动App。这可以节省大量时间。可以使用pytest的fixture(scope=“session”)或unittest的setUpClass来实现。截图与日志是救命稻草:在关键步骤(如断言前)和异常捕获时进行截图(
driver.save_screenshot(‘path.png’))。同时,配置Appium服务器日志输出到文件,便于回溯。封装页面对象模型(Page Object Model, POM):这是UI自动化测试的经典设计模式。将每个页面或重要组件封装成一个类,页面的元素定位器和基本操作作为类的方法。这极大提高了代码的可读性、可维护性和复用性。
class LoginPage: def __init__(self, driver): self.driver = driver self.username_field = (AppiumBy.ACCESSIBILITY_ID, “username”) self.password_field = (AppiumBy.ACCESSIBILITY_ID, “password”) self.login_button = (AppiumBy.ACCESSIBILITY_ID, “loginBtn”) def login(self, username, password): WebDriverWait(self.driver, 10).until( EC.presence_of_element_located(self.username_field) ).send_keys(username) self.driver.find_element(*self.password_field).send_keys(password) self.driver.find_element(*self.login_button).click()在CI/CD中运行:将Appium测试集成到Jenkins、GitLab CI、GitHub Actions等流水线中。关键点:
- 使用
appium --log-level error或--log-timestamp减少日志噪音。 - 使用
appium-driver-xcuitest的--webdriveragent-port指定固定端口,避免冲突。 - 对于模拟器,使用
xcrun simctl命令在CI机器上启动和关闭模拟器。 - 使用
ffmpeg录制测试过程视频,便于失败分析。
- 使用
在我自己的项目实践中,最大的教训就是不要忽视环境的一致性。开发、测试、CI环境的Xcode版本、iOS版本、Appium版本、驱动版本乃至Node.js版本,都尽量保持一致。使用package.json记录Node.js依赖,使用Pipfile或requirements.txt记录Python依赖,能避免很多“在我机器上是好的”这类问题。另外,对于真机测试,维护一套稳定的WDA编译和签名流程文档,并考虑将签名后的WDA IPA包归档,在CI上直接安装,可以绕过每次编译的麻烦。自动化测试的价值在于快速反馈,而稳定可靠是获得这种价值的前提,WebDriver-Agent-Appium这套组合拳,当你摸清它的脾气并妥善配置后,绝对是iOS质量保障体系中不可或缺的强力助手。
