1. 项目概述为什么选择在Mac上搞Appium UI自动化如果你是一名移动端测试工程师或者对自动化感兴趣的后端、前端开发想在Mac上快速搭建一套能同时跑Android和iOS应用的UI自动化测试环境那么这篇实战指南就是为你准备的。Appium作为一款开源的移动端自动化测试框架其“一次编写多端运行”的理念确实很吸引人但新手在Mac上配置环境时往往会被各种依赖、版本冲突和平台差异搞得焦头烂额。我自己在Mac上从零开始搭建、调试再到稳定运行项目踩过的坑不计其数。今天我就把这些经验系统化地梳理出来目标是让你在半天内就能在Mac上跑起第一个Appium自动化测试脚本覆盖Android模拟器和iOS模拟器。核心价值在于“双端”和“快速上手”。很多教程只讲一端或者环境配置步骤散落在各处。我会把Android和iOS在Mac上的配置路径、核心差异、以及如何用一套代码兼容两端的技巧都揉碎了讲清楚。你将了解到的不只是“怎么安装”更是“为什么这么装”以及“装坏了怎么排查”。无论你是想为团队搭建自动化测试基建还是个人学习提升这篇内容都能提供一条清晰、可复现的路径。2. 环境准备与核心工具选型在Mac上玩转双端Appium环境搭建是第一步也是最容易劝退的一步。关键在于理清依赖关系并选择稳定、兼容的版本组合。我的建议是优先使用Homebrew作为包管理器它能极大简化安装流程并处理依赖问题。2.1 基础环境搭建Node.js、Java与开发工具首先我们需要几个跨平台的基础运行时和开发工具。安装Homebrew如果还没安装打开终端Terminal执行以下命令。这是Mac上必备的软件包管理器。/bin/bash -c $(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)安装完成后记得按照终端提示将Homebrew路径添加到你的shell配置文件如~/.zshrc或~/.bash_profile中。安装Node.js和npmAppium Server本身是一个Node.js应用。通过Homebrew安装是最稳妥的方式。brew install node安装后通过node -v和npm -v验证版本。我建议使用当前的LTS长期支持版本即可过新或过旧的版本都可能引入兼容性问题。安装Java JDK运行Android测试需要Java环境特别是用于Android SDK的工具链。同样推荐用Homebrew安装OpenJDK。brew install --cask temurin安装后在终端输入java -version确认安装成功。这里选择Temurin原Adoptium是因为它提供了经过良好测试的OpenJDK发行版兼容性最好。安装IDE编写Python测试脚本我强烈推荐使用PyCharm或VS Code。PyCharm Professional对Python和自动化测试的支持非常完善但专业版需要授权。社区版也足够使用。VS Code免费、轻量通过安装Python、Pytest等插件也能获得极佳的开发体验。在Mac上运行流畅。注意网上有些教程会提到安装“Claude Code”或“Codex”这通常是指特定版本的VS Code或基于其的定制版本。对于自动化测试直接使用官方VS Code是最安全、最稳定的选择避免遇到无法打开或兼容性问题。直接在官网下载macOS版本安装即可。2.2 双端核心SDK安装与配置这是实现“双端”自动化的基石Android和iOS需要不同的工具链。2.2.1 Android环境Android SDK与模拟器Android自动化需要SDK和至少一个虚拟设备AVD。安装Android Studio这是获取Android SDK最便捷的方式。从官网下载安装包.dmg文件拖入应用程序文件夹即可。不必将其作为主要IDE我们主要是用它来下载SDK和管理AVD。配置ANDROID_HOME环境变量Android SDK的路径通常在~/Library/Android/sdk。你需要将此路径告知系统。 打开你的shell配置文件例如~/.zshrc添加以下行export ANDROID_HOME$HOME/Library/Android/sdk export PATH$PATH:$ANDROID_HOME/platform-tools export PATH$PATH:$ANDROID_HOME/tools export PATH$PATH:$ANDROID_HOME/cmdline-tools/latest/bin保存后执行source ~/.zshrc使配置生效。之后在终端输入adb version如果能显示版本号说明配置成功。创建Android虚拟设备AVD打开Android Studio进入“More Actions” - “Virtual Device Manager”。点击“Create device”选择一个设备定义如Pixel 4。在选择系统镜像时建议选择一个非Beta版本的、API Level适中的镜像例如R(API 30) 或Tiramisu(API 33)。下载完成后完成创建。创建后可以在这里启动AVD也可以在终端用命令emulator -avd 你的AVD名称启动。2.2.2 iOS环境Xcode与模拟器iOS自动化只能在macOS上进行因为依赖Xcode。安装Xcode从Mac App Store搜索“Xcode”并安装。这是一个巨大的安装包通常超过10GB请确保网络稳定和磁盘空间充足。安装Xcode Command Line Tools安装完Xcode后必须安装命令行工具。在终端执行xcode-select --install在弹出的窗口中点击“安装”。完成后可通过xcode-select -p查看路径。授权与许可协议首次使用可能需要同意Xcode和命令行工具的许可协议。可以在终端执行sudo xcodebuild -license并按提示操作。启动iOS模拟器安装Xcode后iOS模拟器就已经就绪了。你可以在终端用命令open -a Simulator打开它也可以在“应用程序”文件夹里找到“Simulator”直接打开。建议在运行测试前先手动启动并解锁模拟器。2.3 Appium生态工具安装核心工具是Appium Server和用于元素定位的Inspector。安装Appium Server有两种方式对于新手我推荐方式一。方式一推荐使用Appium Desktop。这是一个图形化应用程序包含了Server和Inspector。从Appium官网的Releases页面下载.dmg文件拖入应用程序文件夹即可。它简化了Server的启动和Inspector的使用。方式二通过npm安装。如果你更喜欢命令行可以执行npm install -g appium安装后通过appium -v检查版本。启动Server只需在终端输入appium。安装Appium DriverAppium 2.0之后需要单独安装驱动Driver。这是实现“双端”的关键。对于Android需要安装uiautomator2驱动这是目前最稳定、功能最全的Android驱动appium driver install uiautomator2对于iOS需要安装xcuitest驱动这是Apple官方支持的iOS UI测试框架appium driver install xcuitest可以通过 appium driver list 查看已安装的驱动。安装Appium Inspector独立版如果你使用Appium Desktop其内置了Inspector。但独立版的Inspector通常更新更及时界面也更现代。可以从Appium Inspector的GitHub Releases页面下载.dmg文件安装。它是连接手机/模拟器查看和获取元素定位信息的必备工具。安装Python客户端库我们的测试脚本将用Python编写。pip install Appium-Python-Client建议在虚拟环境venv中安装避免包冲突。3. 核心配置详解与连接设备环境装好了但要让Appium Server正确“对话”Android和iOS设备还需要进行精准的配置。这部分是很多测试脚本跑不起来的根本原因。3.1 理解Desired CapabilitiesDesired Capabilities是一组发送给Appium Server的键值对用于告诉Server你想启动一个怎样的自动化会话。它是Appium脚本的“启动参数”。双端自动化的大部分差异都体现在这里。一个最基本的Capabilities设置通常包括platformName: 平台名称Android或iOS。platformVersion: 设备系统版本尽量精确。deviceName: 设备名称。Android下可以是自定义的AVD名iOS下是模拟器类型如iPhone 14。app: 待测应用的绝对路径.apk或.ipa或者如果应用已安装则用appPackage和appActivityAndroid或bundleIdiOS。automationName: 自动化引擎。Android通常为uiautomator2iOS为xcuitest。3.2 Android设备连接与配置实战假设我们使用Android Studio创建的Pixel 4 API 33模拟器。启动AVD确保你的模拟器已经启动并进入主界面。获取设备信息在终端输入adb devices你应该能看到类似emulator-5554 device的输出这表示设备已连接。配置CapabilitiesPython示例from appium import webdriver from appium.options.android import UiAutomator2Options # 使用新的Options模式Appium-Python-Client 3.x推荐 options UiAutomator2Options() options.platform_name Android options.platform_version 13 # 根据你的模拟器系统版本填写 options.device_name Pixel_4_API_33 # 你的AVD名称 options.automation_name UiAutomator2 # 方式一指定app路径 options.app /Users/yourname/Downloads/your_app.apk # 方式二如果app已安装指定包名和启动Activity # options.app_package com.example.app # options.app_activity com.example.app.MainActivity options.new_command_timeout 60 # 命令超时时间 # 连接Appium Server默认运行在本地4723端口 driver webdriver.Remote(http://127.0.0.1:4723, optionsoptions)实操心得device_name在Android下不是必须严格匹配但最好保持一致。更关键的是app_package和app_activity可以通过adb shell dumpsys window | grep mCurrentFocus命令在已启动的应用上获取。3.3 iOS模拟器连接与配置实战假设我们使用iPhone 14模拟器系统版本为iOS 16.2。启动模拟器通过open -a Simulator启动并在菜单栏Device中选择iPhone 14和对应的系统版本。安装测试应用将你的.ipa包需要是开发证书签名的拖入模拟器窗口或者使用xcrun simctl install booted path_to_.app命令安装。对于模拟器通常使用.app格式的应用包。配置CapabilitiesPython示例from appium import webdriver from appium.options.ios import XCUITestOptions options XCUITestOptions() options.platform_name iOS options.platform_version 16.2 # 必须与模拟器版本一致 options.device_name iPhone 14 # 必须与模拟器类型一致 options.automation_name XCUITest # 对于iOS通常使用bundle_id来指定已安装的应用 options.bundle_id com.example.app # 或者如果指定.app文件路径首次安装 # options.app /Users/yourname/Downloads/YourApp.app options.new_command_timeout 60 # 注意iOS自动化需要额外的配置来允许WebDriverAgent对模拟器进行控制。 # 通常在第一次运行时需要在模拟器上信任开发者证书。 # 如果遇到权限错误可能需要手动在 设置 - 通用 - VPN与设备管理 中信任证书。 driver webdriver.Remote(http://127.0.0.1:4723, optionsoptions)核心避坑点iOS的platform_version和device_name必须与打开的模拟器完全一致否则Session创建会失败。bundle_id可以在Xcode项目设置中查看或通过xcrun simctl get_app_container booted bundle_id相关命令查询。3.4 使用Appium Inspector定位元素这是编写自动化脚本的核心技能。以独立版Appium Inspector为例。启动Appium Server确保Appium Server正在运行Appium Desktop中点击启动或命令行运行appium。配置并启动Inspector打开Appium Inspector。在“Remote Host”填127.0.0.1端口填4723。在“Desired Capabilities”区域以JSON格式填入与你的测试脚本中完全一致的Capabilities例如上面Android示例的配置。点击“Start Session”。操作与定位如果配置正确Inspector会连接设备并打开应用显示应用界面的截图和层级结构UI树。点击截图上的元素右侧会高亮对应的UI树节点并显示该元素的多种定位信息如resource-id,accessibility-id,xpath,class等。定位策略优先级建议accessibility-id(iOS) /content-desc(Android)这是为无障碍功能设计的ID最稳定且语义化。id(Android的resource-id)通常由开发人员设置唯一性较好。class 其他属性组合的XPath当以上都不存在时使用。尽量避免使用绝对路径或过于复杂的XPath。Inspector可以直接将选中的元素生成多种语言的定位代码片段直接复制到你的脚本中。4. 编写第一个双端兼容的测试脚本现在让我们编写一个简单的测试脚本实现在Android和iOS上执行相同的操作例如打开一个计算器应用并点击按钮。关键在于将平台差异化的部分主要是Capabilities抽象出来。4.1 项目结构与设计思路我们创建一个简单的项目目录appium_quickstart/ ├── conftest.py (可选用于Pytest配置) ├── config/ │ ├── android_caps.json │ └── ios_caps.json ├── pages/ (页面对象模型) │ └── calculator_page.py ├── tests/ │ └── test_calculator.py └── utils/ └── driver_manager.py核心思路驱动管理一个工具类根据传入的平台参数加载对应的Capabilities并创建WebDriver实例。配置分离将Android和iOS的Capabilities分别保存在JSON文件中便于管理和修改。页面对象模型Page Object Model, POM将页面元素和操作封装成类提高代码可读性和可维护性。4.2 驱动管理工具实现utils/driver_manager.pyimport json import os from appium import webdriver from appium.options.android import UiAutomator2Options from appium.options.ios import XCUITestOptions class DriverManager: def __init__(self, platformandroid): self.platform platform.lower() self.driver None def load_capabilities(self): 从配置文件加载Capabilities config_dir os.path.join(os.path.dirname(__file__), .., config) if self.platform android: config_file os.path.join(config_dir, android_caps.json) with open(config_file, r) as f: caps json.load(f) # 转换为Options对象 options UiAutomator2Options() for key, value in caps.items(): setattr(options, key, value) return options elif self.platform ios: config_file os.path.join(config_dir, ios_caps.json) with open(config_file, r) as f: caps json.load(f) options XCUITestOptions() for key, value in caps.items(): setattr(options, key, value) return options else: raise ValueError(fUnsupported platform: {self.platform}) def get_driver(self): 获取WebDriver实例 if self.driver is None: options self.load_capabilities() self.driver webdriver.Remote(http://127.0.0.1:4723, optionsoptions) return self.driver def quit_driver(self): 退出驱动 if self.driver: self.driver.quit() self.driver None4.3 配置文件示例config/android_caps.json{ platformName: Android, platformVersion: 13, deviceName: Pixel_4_API_33, automationName: UiAutomator2, app: /absolute/path/to/your_calculator.apk, appPackage: com.google.android.calculator, appActivity: com.android.calculator2.Calculator, newCommandTimeout: 60, noReset: false }config/ios_caps.json{ platformName: iOS, platformVersion: 16.2, deviceName: iPhone 14, automationName: XCUITest, bundleId: com.apple.calculator, newCommandTimeout: 60, noReset: false }注意这里使用了系统自带计算器的包名。对于你自己的应用需要替换成实际的app路径或bundleId。noReset设为false意味着每次会话开始前都会重置应用状态。4.4 页面对象与测试用例pages/calculator_page.pyfrom appium.webdriver.common.appiumby import AppiumBy class CalculatorPage: def __init__(self, driver): self.driver driver # 使用通用的定位策略accessibility_id优先fallback到其他 # 这里以数字2和加号为例实际定位符需用Inspector获取 self._button_two (AppiumBy.ACCESSIBILITY_ID, 2) # iOS # 对于Android如果accessibility_id没有可能需要用resource-id # self._button_two (AppiumBy.ID, com.google.android.calculator:id/digit_2) self._button_plus (AppiumBy.ACCESSIBILITY_ID, add) self._button_equals (AppiumBy.ACCESSIBILITY_ID, equals) self._result_field (AppiumBy.ID, result) # 假设结果框的ID是result def click_two(self): self.driver.find_element(*self._button_two).click() def click_plus(self): self.driver.find_element(*self._button_plus).click() def click_equals(self): self.driver.find_element(*self._button_equals).click() def get_result(self): return self.driver.find_element(*self._result_field).texttests/test_calculator.pyimport pytest import sys import os sys.path.append(os.path.join(os.path.dirname(__file__), ..)) from utils.driver_manager import DriverManager from pages.calculator_page import CalculatorPage class TestCalculator: pytest.fixture(scopeclass) def driver(self, request): # 通过命令行参数或环境变量指定平台例如pytest --platformios platform request.config.getoption(--platform, defaultandroid) dm DriverManager(platformplatform) driver dm.get_driver() yield driver dm.quit_driver() def test_addition(self, driver): calc_page CalculatorPage(driver) calc_page.click_two() calc_page.click_plus() calc_page.click_two() calc_page.click_equals() result calc_page.get_result() # 注意不同计算器显示格式可能不同这里断言可能需调整 assert 4 in result def pytest_addoption(parser): parser.addoption( --platform, actionstore, defaultandroid, helpplatform to run tests on: android or ios )运行测试 在终端中进入项目根目录执行# 运行Android测试 pytest tests/test_calculator.py --platformandroid -v # 运行iOS测试 pytest tests/test_calculator.py --platformios -v5. 常见问题排查与实战技巧即使按照步骤操作你也可能会遇到各种问题。这里我整理了最常遇到的“坑”及其解决方案。5.1 环境与连接类问题问题现象可能原因排查步骤与解决方案adb devices列表为空1. AVD未启动。2. ADB服务异常。3. 设备未授权真机。1. 确认模拟器已启动。2. 重启ADBadb kill-server adb start-server。3. 真机需开启USB调试并点击授权弹窗。Appium Server启动失败端口被占用4723端口被其他进程占用。1. 查找占用进程lsof -i :4723。2. 终止该进程或修改Appium启动端口appium -p 4724。创建Session失败报错Unable to find a matching set of capabilities1. 请求的Capabilities与Appium Server配置不匹配。2. 未安装对应的Driver。1. 检查Capabilities拼写和值是否正确特别是platformVersion和deviceName。2. 运行appium driver list确认uiautomator2和xcuitest已安装且状态为installed。iOS Session创建失败报权限错误WebDriverAgentWDA未正确签名或信任。1.对于模拟器首次运行通常需要手动在模拟器上信任证书。在模拟器中进入设置-通用-VPN与设备管理或描述文件与设备管理找到开发者应用证书并信任。2.对于真机需要配置Xcode自动管理签名并使用有效的开发者账号。过程较复杂建议先从模拟器开始。Inspector无法连接设备1. Appium Server未运行。2. Capabilities配置错误。3. 设备未准备好。1. 确认Server已启动且日志无报错。2. 核对Inspector中填写的Capabilities与脚本中完全一致。3. 确保设备模拟器已启动并处于解锁状态。5.2 脚本执行类问题问题现象可能原因排查步骤与解决方案找不到元素NoSuchElementException1. 定位符错误或不唯一。2. 元素尚未加载出来页面未就绪。3. 应用有弹窗、引导页遮挡。1. 使用Inspector重新确认定位符优先使用accessibility-id或id。2. 添加显式等待WebDriverWait等待元素出现再操作。3. 脚本开始时增加处理常见弹窗的逻辑。元素可找到但点击无效1. 元素非可点击状态。2. 点在了元素边缘或坐标不准。3. 需要特殊操作如长按、滑动。1. 检查元素属性如clickable,enabled。2. 尝试使用driver.tap([(x, y)])或driver.execute_script(mobile: tap, {x: x, y: y})。3. 使用TouchAction或W3C Actions API进行复杂手势操作。测试在iOS和Android上行为不一致1. 应用本身UI/交互有差异。2. 定位符策略未做平台适配。3. 系统控件行为不同。1. 这是双端自动化的常态。需要在POM层做抽象为不同平台提供不同的定位符或操作实现。2. 在页面对象类中可以根据driver.capabilities[platformName]来判断平台执行不同的查找或操作逻辑。脚本运行缓慢1. 使用了不稳定的定位方式如复杂XPath。2. 隐式等待时间设置过长。3. 未使用ID等快速定位方式。1. 优化定位策略多用ID和accessibility-id。2. 合理设置隐式等待driver.implicitly_wait(10)并多用显式等待替代固定的sleep。3. 对于列表等动态内容考虑使用mobile: scroll等原生方法。5.3 独家实战技巧与心得Capabilities管理进阶对于大型项目Capabilities可能因环境开发、测试、生产、设备、应用版本而异。可以考虑使用python-dotenv管理环境变量或者用YAML文件来组织多套配置然后在DriverManager中根据运行时参数动态加载。等待策略是稳定的关键永远不要使用固定的time.sleep()。组合使用隐式等待和显式等待。from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from appium.webdriver.common.appiumby import AppiumBy # 隐式等待全局 driver.implicitly_wait(10) # 显式等待针对特定元素和条件 wait WebDriverWait(driver, 15) element wait.until(EC.presence_of_element_located((AppiumBy.ID, some_id))) element.click()日志与截图是救命稻草务必在测试框架中集成失败自动截图功能。Pytest可以通过pytest_runtest_makereport钩子实现。同时开启Appium Server的详细日志--log-level debug在排查问题时能提供大量信息。真机测试注意事项Android真机确保开启“开发者选项”和“USB调试”。有时需要额外开启“禁止权限监控”等选项才能正常自动化。iOS真机需要Apple开发者账号在Xcode中配置自动签名并将设备UDID添加到配置文件中。过程繁琐建议团队有专人负责证书管理。并行测试当用例多起来后可以考虑并行运行。需要启动多个Appium Server实例绑定不同端口并使用pytest-xdist等插件分发测试任务。每个Driver实例连接不同的Server端口和设备。在Mac上搭建和运行双端Appium UI自动化初期配置确实需要一些耐心但一旦环境打通后续的脚本编写和扩展就会顺畅很多。核心在于理解Android和iOS两套生态工具链的差异并用代码将平台特性抽象隔离。从简单的单个操作开始逐步封装成页面对象再构建成完整的测试套件你会发现自动化测试带来的回报——尤其是回归测试的效率提升——是非常显著的。如果在搭建过程中遇到上面未覆盖的奇怪问题多查看Appium Server的日志那里面通常藏着答案。