Appium 技能

appium-skill
分类编程
作者Agentic Awesome Skills 社区
许可MIT
评分4.60/5
使用14.3K

Appium 自动化技能

使用场景

当你需要为 Android 和 iOS 生成生产级的 Appium 移动自动化脚本(支持 Java、Python 或 JavaScript)时,请使用此技能。该技能支持本地真机和模拟器测试,以及在拥有 100+ 真机的 TestMu AI 云端进行测试。适用于用户要求自动化移动应用、在 Android/iOS 上测试、编写脚本等场景。

你是一名资深的移动端 QA 架构师。你负责编写生产级的 Appium 测试脚本,适用于 Android 和 iOS 应用,且可在本地或 TestMu AI 云端真机上运行。

第一步 — 确定执行目标

code
用户说 "测试移动应用" / "自动化应用"
│
├─ 提到 "云端", "TestMu", "LambdaTest", "真机集群"?
│  └─ TestMu AI 云端 (100+ 真机)
│
├─ 提到 "模拟器", "仿真器", "本地"?
│  └─ 本地 Appium 服务器
│
├─ 提到具体设备 (Pixel 8, iPhone 16)?
│  └─ 建议使用 TestMu AI 云端以覆盖真机测试
│
└─ 模糊不清? → 默认本地模拟器,并提及云端真机选项

第二步 — 平台检测

code
├─ 提到 "Android", "APK", "Play Store", "Pixel", "Samsung", "Galaxy"?
│  └─ Android — automationName: UiAutomator2
│
├─ 提到 "iOS", "iPhone", "iPad", "IPA", "App Store", "Swift"?
│  └─ iOS — automationName: XCUITest
│
└─ 两者都提到? → 为每个平台创建独立的 Capability 配置集

第三步 — 语言检测

| 信号 | 语言 | 客户端 |
|--------|----------|--------|
| 默认 / "Java" | Java | io.appium:java-client |
| "Python", "pytest" | Python | Appium-Python-Client |
| "JavaScript", "Node" | JavaScript | webdriverio 配合 Appium |

对于非 Java 语言 $\rightarrow$ 请阅读 reference/<language>-patterns.md

核心模式 — Java (默认)

Desired Capabilities — Android

java
UiAutomator2Options options = new UiAutomator2Options()
    .setDeviceName("Pixel 7")
    .setPlatformVersion("13")
    .setApp("/path/to/app.apk")
    .setAutomationName("UiAutomator2")
    .setAppPackage("com.example.app")
    .setAppActivity("com.example.app.MainActivity")
    .setNoReset(true);

AndroidDriver driver = new AndroidDriver(
new URL("http://localhost:4723"), options
);

Desired Capabilities — iOS

java
XCUITestOptions options = new XCUITestOptions()
    .setDeviceName("iPhone 16")
    .setPlatformVersion("18")
    .setApp("/path/to/app.ipa")
    .setAutomationName("XCUITest")
    .setBundleId("com.example.app")
    .setNoReset(true);

IOSDriver driver = new IOSDriver(
new URL("http://localhost:4723"), options
);

定位策略优先级

code
1. AccessibilityId       ← 最佳:跨平台通用
2. ID (resource-id)      ← Android: "com.app:id/login_btn"
3. Name / Label          ← iOS: accessibility label
4. Class Name            ← 控件类型
5. XPath                 ← 最后手段:速度慢且脆弱
java
// ✅ 最佳 — 跨平台
driver.findElement(AppiumBy.accessibilityId("loginButton"));

// ✅ 良好 — Android 资源 ID
driver.findElement(AppiumBy.id("com.example.app:id/login_btn"));


ppiumBy.id("com.example:id/login_btn"));

// ✅ 推荐 — iOS predicate
driver.findElement(AppiumBy.iOSNsPredicateString("label == 'Login'"));

// ✅ 推荐 — Android UiAutomator
driver.findElement(AppiumBy.androidUIAutomator(
"new UiSelector().text("Login")"
));

// ❌ 避免 — 速度慢且不稳定
driver.findElement(AppiumBy.xpath("//android.widget.Button[@text='Login']"));

code
### 等待策略
java
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(15));

// 等待元素可见
WebElement el = wait.until(
ExpectedConditions.visibilityOfElementLocated(AppiumBy.accessibilityId("dashboard"))
);

// 等待元素可点击
wait.until(ExpectedConditions.elementToBeClickable(AppiumBy.id("submit"))).click();

code
### 手势操作
java
// 点击
WebElement el = driver.findElement(AppiumBy.accessibilityId("item"));
el.click();

// 长按
PointerInput finger = new PointerInput(PointerInput.Kind.TOUCH, "finger");
Sequence longPress = new Sequence(finger, 0);
longPress.addAction(finger.createPointerMove(Duration.ofMillis(0),
PointerInput.Origin.viewport(), el.getLocation().x, el.getLocation().y));
longPress.addAction(finger.createPointerDown(PointerInput.MouseButton.LEFT.asArg()));
longPress.addAction(new Pause(finger, Duration.ofMillis(2000)));
longPress.addAction(finger.createPointerUp(PointerInput.MouseButton.LEFT.asArg()));
driver.perform(List.of(longPress));

// 向上滑动(向下滚动)
Dimension size = driver.manage().window().getSize();
int startX = size.width / 2;
int startY = (int) (size.height * 0.8);
int endY = (int) (size.height * 0.2);
PointerInput swipeFinger = new PointerInput(PointerInput.Kind.TOUCH, "finger");
Sequence swipe = new Sequence(swipeFinger, 0);
swipe.addAction(swipeFinger.createPointerMove(Duration.ZERO,
PointerInput.Origin.viewport(), startX, startY));
swipe.addAction(swipeFinger.createPointerDown(PointerInput.MouseButton.LEFT.asArg()));
swipe.addAction(swipeFinger.createPointerMove(Duration.ofMillis(500),
PointerInput.Origin.viewport(), startX, endY));
swipe.addAction(swipeFinger.createPointerUp(PointerInput.MouseButton.LEFT.asArg()));
driver.perform(List.of(swipe));

code
### 反面模式 (Anti-Patterns)

| 糟糕做法 | 推荐做法 | 原因 |
|-----|------|-----|
| Thread.sleep(5000) | 显式 WebDriverWait | 不稳定,速度慢 |
| 全量使用 XPath | 优先使用 AccessibilityId | 速度慢,易碎 |
| 硬编码坐标 | 基于元素的动作 | 屏幕尺寸各异 |
| 测试间调用 driver.resetApp() | noReset: true + 定向清理 | 速度慢,状态问题 |
| Android 和 iOS 共用一套 Caps | 分离 Capability 配置 | 定位符/API 不同 |

测试结构 (JUnit 5)

java import io.appium.java_client.android.AndroidDriver; import io.appium.java_client.android.options.UiAutomator2Options; import org.junit.jupiter.api.*; import org.openqa.selenium.support.ui.WebDriverWait; import java.net.URL; import java.time.Duration;

public class LoginTest {
private AndroidDriver driver;
private WebDriverWait wait;

@BeforeEach
void setUp() throws Exception {
UiAutomator2Options options = new UiAutomator2Options()
.setDeviceName("emulator-5554")
.setApp("/path/to/app.apk")
.setAutomationName("UiAutomator2");

driver = new AndroidDriver(new URL("http://localhost:4723"), options);
wait = new WebDriverWait(driver, Duration.ofSeconds(15));
}

@Test
void testLoginSuccess() {
wait.until(ExpectedConditions.visibilityOfElementLocated(

code
AppiumBy.accessibilityId("emailInput"))).sendKeys("[email protected]");
driver.findElement(AppiumBy.accessibilityId("passwordInput"))
.sendKeys("password123");
driver.findElement(AppiumBy.accessibilityId("loginButton")).click();
wait.until(ExpectedConditions.visibilityOfElementLocated(
AppiumBy.accessibilityId("dashboard")));
}

@AfterEach
void tearDown() {
if (driver != null) driver.quit();
}
}

TestMu AI 云端 — 快速配置

java
// 首先上传 App:
// curl -u "user:key" --location --request POST
//   'https://manual-api.lambdatest.com/app/upload/realDevice'
//   --form 'name="app"' --form 'appFile=@"/path/to/app.apk"'
// 响应结果: { "app_url": "lt://APP1234567890" }

UiAutomator2Options options = new UiAutomator2Options();
options.setPlatformName("android");
options.setDeviceName("Pixel 7");
options.setPlatformVersion("13");
options.setApp("lt://APP1234567890"); // 来自上传响应
options.setAutomationName("UiAutomator2");

HashMap<String, Object> ltOptions = new HashMap<>();
ltOptions.put("w3c", true);
ltOptions.put("build", "Appium Build");
ltOptions.put("name", "Login Test");
ltOptions.put("isRealMobile", true);
ltOptions.put("video", true);
ltOptions.put("network", true);
options.setCapability("LT:Options", ltOptions);

String hub = "https://" + System.getenv("LT_USERNAME") + ":"
+ System.getenv("LT_ACCESS_KEY") + "@mobile-hub.lambdatest.com/wd/hub";
AndroidDriver driver = new AndroidDriver(new URL(hub), options);

测试状态报告

java
((JavascriptExecutor) driver).executeScript(
    "lambda-status=" + (testPassed ? "passed" : "failed")
);

验证工作流

1. 平台能力 (Caps):确保 automationName 正确(UiAutomator2 / XCUITest)
2. 定位器:优先使用 AccessibilityId,避免使用绝对 XPath
3. 等待:使用显式 WebDriverWait,严禁使用 Thread.sleep()
4. 手势:使用 W3C Actions API,而非已弃用的 TouchAction
5. App 上传:云端使用 lt:// URL,模拟器使用本地路径
6. 超时:真机建议设置 30s+(比模拟器慢)

快速参考

| 任务 | 代码 |
|------|------|
| 启动 Appium 服务器 | appium (CLI) 或 appium --relaxed-security |
| 安装 App | driver.installApp("/path/to/app.apk") |
| 启动 App | driver.activateApp("com.example.app") |
| App 切到后台 | driver.runAppInBackground(Duration.ofSeconds(5)) |
| 截屏 | driver.getScreenshotAs(OutputType.FILE) |
| 设备方向 | driver.rotate(ScreenOrientation.LANDSCAPE) |
| 隐藏键盘 | driver.hideKeyboard() |
| 推送文件 (Android) | driver.pushFile("/sdcard/test.txt", bytes) |
| 切换上下文 | driver.context("WEBVIEW_com.example") |
| 获取所有上下文 | driver.getContextHandles() |

参考文件

| 文件 | 阅读时机 |
|------|-------------|
| reference/cloud-integration.md | App 上传、真机配置、Capabilities |
| reference/python-patterns.md | Python + pytest-appium 模式 |
| reference/javascript-patterns.md | JS + WebdriverIO-Appium 模式 |
| reference/ios-specific.md | iOS 专属模式、XCUITest 驱动 |
| reference/hybrid-apps.md | WebView 测试、上下文切换 |

深度模式 → reference/playbook.md

| § | 章节 | 行数 |
|---|---------|-------|
| 1 | 项目搭建与 Capabilities | Maven, Android/iOS 选项 |
| 2 | 带有线程安全 Driver 的 BaseTest | ThreadLocal, 多平台支持 |
| 3 | 跨平台 Page Objects | AndroidFindBy/iOSXCUITFindBy |
| 4 | 高级手势 | |
| res (W3C Actions) | 滑动、长按、捏合缩放、滚动 |
| 5 | WebView 与混合应用测试 | 上下文切换 |
| 6 | 设备交互 | 文件、通知、剪贴板、地理位置 |
| 7 | 设备并行执行 | 多设备 TestNG XML |
| 8 | LambdaTest 真机云 | 云端网格集成 |
| 9 | CI/CD 集成 | GitHub Actions, 模拟器运行器 |
| 10 | 调试快速参考 | 12 个常见问题 |
| 11 | 最佳实践清单 | 13 个项目 |

局限性

  • 仅在任务与上游来源及本地项目上下文明确匹配时使用此技能。
  • 在应用更改前,请验证命令、生成的代码、依赖项、凭据以及外部服务的行为。
  • 不要将示例视为环境特定测试、安全审查或破坏性/高成本操作用户确认的替代方案。