Appium 移动端 UI 自动化测试入门
mermaid flowchart LR A[测试脚本 (Python/Java...)] --> B[Appium Server] B --> C[UiAutomator2 服务器 (Android)] B --> D[XCUITest 服务器 (iOS)]
1. **测试脚本**:通过 Appium 客户端发送 HTTP 请求到 Appium 服务器,请求遵循 JSON Wire Protocol。
2. **Appium 服务器**:基于 Node.js 实现的 HTTP 服务器,翻译并转发命令给目标设备的自动化引擎。
3. **设备端引擎**:
- Android:在设备上运行 **UiAutomator2** 服务,执行具体的点击、滑动等操作。
- iOS:通过 **XCUITest** 框架与设备上的应用交互。
这种架构使得一台 Appium 服务器可以远程控制多台真机或模拟器,非常适合分布式测试执行。
## 环境准备(适用于 Android)
### 安装 Appium 环境
选择 **Appium Desktop**(图形化界面)或命令行工具 `appium`。推荐初学者从桌面版开始。
1. **下载 Appium Desktop**
访问 [GitHub Releases](https://github.com/appium/appium-desktop/releases) 页面,下载适合你操作系统的安装包并安装。
2. **(可选) 安装 Node.js 及 Appium 命令行**
如果你希望使用 CLI 方式启动服务器:
```bash
npm install -g appium
appium driver install uiautomator2 # 安装 Android 驱动
Android 测试环境配置
- 安装 JDK 8 或以上版本,并配置
JAVA_HOME环境变量。 - 安装 Android Studio,确保包含 Android SDK Platform-Tools 和至少一个系统镜像。
- 设置
ANDROID_HOME环境变量,指向 SDK 路径(如~/Android/Sdk),并将$ANDROID_HOME/platform-tools和$ANDROID_HOME/tools加入PATH。 - 创建 Android 模拟器(AVD)或准备一台开启了 USB 调试的真机。连接后运行
adb devices确认设备已识别。
iOS 测试环境配置(仅针对 macOS)
- 安装 Xcode 及命令行工具,配置开发者证书。
- Appium 需要 Carthage 依赖管理工具:
brew install carthage。 - 对于真机测试,还需要安装 ios-deploy:
npm install -g ios-deploy。 - 模拟器可直接使用,无需额外配置。
第一个测试脚本(Python + 计算器 App)
以下示例使用 Python 调用 Appium 来自动化 Android 原生计算器,执行简单的加法并验证结果。
安装 Python 客户端
pip install Appium-Python-Client
准备待测应用
使用 Android 系统自带的计算器应用(不同系统包名稍有差异,本示例使用原生 Android 计算器 com.android.calculator2)。确保模拟器或真机上计算器 App 已存在。
启动 Appium 服务器
打开 Appium Desktop,点击「Start Server」使用默认参数启动服务器(http://127.0.0.1:4723)。
如果使用 CLI,直接执行 appium 命令。
编写测试脚本
from appium import webdriver
from appium.webdriver.common.appiumby import AppiumBy
import time
# 配置 Desired Capabilities
desired_caps = {
'platformName': 'Android',
'deviceName': 'emulator-5554', # 可通过 adb devices 获取
'platformVersion': '13.0',
'appPackage': 'com.android.calculator2',
'appActivity': '.Calculator',
'automationName': 'UiAutomator2',
'noReset': True # 不重置应用数据
}
# 创建会话
driver = webdriver.Remote('http://localhost:4723', desired_caps)
time.sleep(3) # 等待界面加载
# 定位元素并执行加法:5 + 3 = 8
digit_5 = driver.find_element(AppiumBy.ID, "com.android.calculator2:id/digit_5")
digit_5.click()
plus_btn = driver.find_element(AppiumBy.ACCESSIBILITY_ID, "plus")
plus_btn.click()
digit_3 = driver.find_element(AppiumBy.ID, "com.android.calculator2:id/digit_3")
digit_3.click()
equal_btn = driver.find_element(AppiumBy.ACCESSIBILITY_ID, "equals")
equal_btn.click()
# 获取结果并断言
result = driver.find_element(AppiumBy.ID, "com.android.calculator2:id/result")
assert result.text == "8", f"Expect 8, but got {result.text}"
print("测试通过!")
driver.quit()
代码解读
Desired Capabilities:告诉 Appium 服务器要连接的设备、平台以及应用入口信息。- 会话创建:
webdriver.Remote返回一个客户端对象,后续所有操作都由它代理。 - 元素定位:使用
ID或ACCESSIBILITY_ID(推荐)唯一定位控件。Accessibility ID 相当于元素的content-desc属性,比 ID 更稳定。 - 操作:
click()点击,text获取文本。 - 断言:验证结果是否符合预期。
元素定位与 Appium Inspector
元素定位是 UI 自动化的基石,Appium 提供了强大的 Inspector 工具来简化定位过程。
使用 Appium Inspector
- 确保 Appium 服务器正在运行。
- 打开 Inspector,填入与测试脚本相同的 Desired Capabilities。
- 点击「Start Session」启动检查器。应用界面截图会显示在左侧,右侧是元素树和属性面板。
- 点击界面上的任何元素,即可看到它的所有属性:
resource-id、content-desc、class、xpath等。
常用定位策略
| 策略 | 使用方法 | 推荐程度 | 说明 |
|---|---|---|---|
| Accessibility ID | AppiumBy.ACCESSIBILITY_ID, "plus" |
⭐⭐⭐⭐⭐ | 对应 content-desc,最稳定,跨平台通用 |
| ID | AppiumBy.ID, "..digit_5" |
⭐⭐⭐⭐ | 利用 resource-id,唯一性较好 |
| Class Name | AppiumBy.CLASS_NAME, "android.widget.Button" |
⭐⭐ | 多个同类元素时需进一步过滤 |
| XPath | AppiumBy.XPATH, "//android.widget.Button[1]" |
⭐⭐ | 灵活但性能稍差,结构变动易失效 |
| Android UIAutomator | new UiSelector().text("5") |
⭐⭐⭐ | 仅限 Android,功能强大 |
| iOS Predicate String | name == "plus" |
⭐⭐⭐⭐ | 仅限 iOS,类似于查询语句 |
最佳实践:优先让开发人员在控件上添加有意义的 content-desc 或 accessibilityIdentifier,然后用 Accessibility ID 定位。
常用操作与等待机制
基础手势
# 点击
element.click()
# 输入文本
element.send_keys("hello")
# 滑动(屏幕坐标滑动)
driver.swipe(start_x, start_y, end_x, end_y, duration=800)
# 将元素滑动到可见区域 (仅Android)
driver.execute_script("mobile: scroll", {"element": element.id})
# 后退
driver.back()
获取属性与状态
text = element.text
enabled = element.is_enabled()
displayed = element.is_displayed()
location = element.location # {'x': x, 'y': y}
size = element.size # {'width': w, 'height': h}
显式等待(推荐)
避免使用固定的 time.sleep(),引入 WebDriverWait 等待特定条件成立。
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
wait = WebDriverWait(driver, 10)
result_element = wait.until(EC.presence_of_element_located((AppiumBy.ID, "result")))
常用的预期条件:visibility_of_element_located、element_to_be_clickable、text_to_be_present_in_element 等。
隐式等待
设置一个全局等待时间,当查找元素时如果找不到,会轮询等待直到超时。
driver.implicitly_wait(5) # 单位秒
注意:隐式等待和显式等待一般不要混用,以免造成不可预期的等待时间。
进阶话题
处理混合应用与 WebView
如果应用内嵌了 WebView(网页内容),需要切换上下文才能控制页面中的元素。
- 获取所有可用上下文:
contexts = driver.contexts # 通常包括:NATIVE_APP, WEBVIEW_xxx - 切换到 WebView 上下文:
driver.switch_to.context('WEBVIEW_com.example') - 此时你可以使用标准的 WebDriver 方法定位网页元素(如
id、css selector)。 - 操作完成后切回原生:
driver.switch_to.context('NATIVE_APP')
集成测试框架(pytest)
将 Appium 测试融入测试框架,方便管理和报告。
import pytest
from appium import webdriver
@pytest.fixture(scope="module")
def driver():
caps = { ... }
d = webdriver.Remote('http://localhost:4723', caps)
yield d
d.quit()
def test_add(driver):
driver.find_element(AppiumBy.ID, "digit_2").click()
# ... 执行操作与断言
并发测试
Appium 本身支持多会话,你可以用 Selenium Grid 或者 Appium Grid 来管理多个设备并行执行测试。结合 pytest-xdist 等插件,可以显著缩短测试执行时间。
常见问题与调试技巧
-
连接报错:Could not find a connected Android device
- 检查
adb devices是否列出设备。 - 确认 USB 调试已开启,授权了该计算机。
- 检查
-
元素定位失败(NoSuchElementException)
- 使用 Inspector 确认元素是否在当前页面。
- 增加显式等待,确保元素已渲染完毕。
- 尝试不同的定位策略,如从 ID 改为 Accessibility ID。
-
操作无效(点击没反应)
- 元素可能被其他图层遮挡,尝试先滑动屏幕使其可见。
- 有些控件需要先获得焦点,尝试
element.click()前执行element.send_keys("")(仅 Android 某些场景)。
-
调试利器:截图与日志
driver.save_screenshot("error.png") # 保存当前屏幕截图