1. 环境准备:搭建你的第一个Appium测试环境
想要玩转Appium自动化测试,第一步就是把环境搭建好。这就像你要做菜,得先把厨房收拾利索,锅碗瓢盆准备齐全。我见过不少新手在这第一步就卡壳,其实只要跟着步骤来,半小时就能搞定。
Java环境配置是基础中的基础。推荐安装JDK 1.8或更高版本,安装完成后记得配置JAVA_HOME环境变量。验证方法很简单,打开命令行输入java -version,能看到版本号就说明成功了。
接下来是Node.js安装,因为Appium服务是基于Node.js运行的。直接从官网下载LTS版本安装,安装完成后在命令行输入node -v和npm -v,能显示版本号就说明安装正确。
Android环境配置稍微复杂些:
- 下载Android Studio,安装时勾选Android SDK
- 配置ANDROID_HOME环境变量指向SDK安装路径
- 把platform-tools和tools目录添加到PATH中
- 通过
adb devices命令验证是否能识别连接的设备
最后是Appium安装,直接通过npm安装最方便:
npm install -g appium安装完成后运行appium -v查看版本,然后启动服务:
appium看到"Appium REST http interface listener started"就说明服务启动成功了。
提示:如果遇到端口冲突,可以用
appium -p 4725指定其他端口
2. 项目配置:创建你的第一个测试框架
环境搭好了,现在来创建测试项目。我推荐使用Maven来管理Java项目,它能自动处理依赖关系,省去很多麻烦。
在pom.xml中添加这些关键依赖:
<dependencies> <!-- Appium Java客户端 --> <dependency> <groupId>io.appium</groupId> <artifactId>java-client</artifactId> <version>9.4.0</version> </dependency> <!-- TestNG测试框架 --> <dependency> <groupId>org.testng</groupId> <artifactId>testng</artifactId> <version>7.8.0</version> <scope>test</scope> </dependency> </dependencies>项目结构建议这样组织:
src ├── main │ └── java │ └── com │ └── yourcompany │ └── utils # 工具类 └── test └── java └── com └── yourcompany ├── pages # 页面对象 └── tests # 测试用例基础测试类可以这样写:
public class BaseTest { protected AppiumDriver driver; protected WebDriverWait wait; @BeforeTest public void setUp() throws MalformedURLException { UiAutomator2Options options = new UiAutomator2Options() .setPlatformVersion("13") .setDeviceName("Pixel_5") .setAppPackage("com.example.app") .setAppActivity(".MainActivity"); driver = new AndroidDriver(new URL("http://127.0.0.1:4723"), options); wait = new WebDriverWait(driver, Duration.ofSeconds(10)); } @AfterTest public void tearDown() { if (driver != null) { driver.quit(); } } }3. 元素定位:掌握Appium的核心技能
元素定位是自动化测试的基石,就像做手术要找准位置一样。Appium支持多种定位方式,每种都有适用场景。
ID定位是最可靠的方式:
driver.findElement(By.id("com.example:id/login_button"));XPath定位更灵活但性能稍差:
// 绝对路径(不推荐) driver.findElement(By.xpath("/hierarchy/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.Button")); // 相对路径(推荐) driver.findElement(By.xpath("//android.widget.Button[@text='登录']"));UIAutomator定位是Android专属:
driver.findElement(By.androidUIAutomator("new UiSelector().text(\"登录\")"));实际项目中我建议:
- 优先使用resource-id定位
- 没有id时用accessibility id
- 复杂场景用相对路径XPath
- 避免使用绝对路径和坐标定位
注意:元素可能有延迟加载,记得添加显式等待:
WebElement loginBtn = wait.until(ExpectedConditions.presenceOfElementLocated( By.id("com.example:id/login_button")));4. 测试实战:从简单计算器到企业级应用
现在我们来实战测试一个计算器应用。这个例子虽然简单,但包含了自动化测试的核心要素。
首先获取应用信息:
# 获取当前Activity adb shell dumpsys window | grep mCurrent # 获取包名和主Activity adb shell dumpsys package com.example.calculator测试类继承BaseTest:
public class CalculatorTest extends BaseTest { @Test public void testAddition() { // 输入5 driver.findElement(By.id("com.example.calculator:id/five")).click(); // 点击加号 driver.findElement(By.id("com.example.calculator:id/plus")).click(); // 输入3 driver.findElement(By.id("com.example.calculator:id/three")).click(); // 点击等号 driver.findElement(By.id("com.example.calculator:id/equals")).click(); // 验证结果 String result = driver.findElement(By.id("com.example.calculator:id/result")).getText(); Assert.assertEquals(result, "8"); } }企业级应用测试更复杂,需要处理:
- 登录状态管理
- 网络请求Mock
- 数据准备和清理
- 多设备并行测试
例如电商应用测试:
public class ECommerceTest extends BaseTest { @Test public void testAddToCart() { // 登录 login("testuser", "password123"); // 搜索商品 searchProduct("iPhone 15"); // 加入购物车 addToCart(); // 验证购物车 Assert.assertTrue(isProductInCart("iPhone 15")); } private void login(String username, String password) { // 实现登录逻辑 } // 其他辅助方法... }5. 高级技巧:让测试更稳定高效
当基础测试跑通后,就该考虑如何提升测试的稳定性和效率了。这是我多年实践总结的几个关键技巧。
Page Object模式是必须掌握的,它把页面元素和操作封装成类:
public class LoginPage { private final AppiumDriver driver; // 元素定位器 private final By usernameField = By.id("com.example:id/username"); private final By passwordField = By.id("com.example:id/password"); private final By loginButton = By.id("com.example:id/login"); public LoginPage(AppiumDriver driver) { this.driver = driver; } public HomePage login(String username, String password) { driver.findElement(usernameField).sendKeys(username); driver.findElement(passwordField).sendKeys(password); driver.findElement(loginButton).click(); return new HomePage(driver); } }数据驱动测试可以让一个测试方法运行多组数据:
@DataProvider(name = "loginData") public Object[][] provideLoginData() { return new Object[][] { {"correctUser", "correctPass", true}, {"wrongUser", "wrongPass", false} }; } @Test(dataProvider = "loginData") public void testLogin(String user, String pass, boolean expected) { boolean actual = loginPage.login(user, pass).isLoginSuccessful(); Assert.assertEquals(actual, expected); }并行测试能大幅缩短执行时间。在testng.xml中配置:
<suite name="Parallel Tests" parallel="tests" thread-count="3"> <test name="Android Test"> <classes> <class name="com.example.AndroidTests"/> </classes> </test> <test name="iOS Test"> <classes> <class name="com.example.iOSTests"/> </classes> </test> </suite>异常处理也很重要,比如处理元素找不到的情况:
public WebElement safeFind(By locator) { try { return wait.until(ExpectedConditions.presenceOfElementLocated(locator)); } catch (TimeoutException e) { takeScreenshot("element_not_found"); throw new RuntimeException("元素定位失败: " + locator, e); } }6. CI/CD集成:让自动化测试成为开发流程的一部分
自动化测试最终要融入CI/CD流程才能真正发挥价值。我分享几个实际项目中的集成方案。
Jenkins集成是最常见的做法。配置一个Jenkins任务:
- 代码检出后执行Maven命令:
mvn clean test -Dplatform=android -Ddevice=emulator- 生成Allure报告:
allure serve allure-resultsGitLab CI配置示例:
stages: - test appium_test: stage: test image: openjdk:11 services: - name: appium/appium alias: appium script: - apt-get update && apt-get install -y android-sdk - export ANDROID_HOME=/usr/lib/android-sdk - mvn test artifacts: when: always paths: - target/surefire-reports/ - screenshots/测试报告很重要,Allure报告可以展示:
- 测试通过率
- 失败原因
- 执行耗时
- 截图和日志
在pom.xml中添加Allure配置:
<plugin> <groupId>io.qameta.allure</groupId> <artifactId>allure-maven</artifactId> <version>2.12.0</version> </plugin>云测试平台集成方案:
// BrowserStack配置示例 DesiredCapabilities caps = new DesiredCapabilities(); caps.setCapability("browserstack.user", "your_username"); caps.setCapability("browserstack.key", "your_accesskey"); caps.setCapability("app", "bs://app_hash"); caps.setCapability("device", "Samsung Galaxy S22"); caps.setCapability("os_version", "12.0"); AppiumDriver driver = new AppiumDriver( new URL("http://hub.browserstack.com/wd/hub"), caps);7. 企业级实践:应对复杂场景的解决方案
在企业级应用中,你会遇到各种复杂场景,需要更高级的解决方案。
多应用切换测试方案:
// 启动第一个应用 driver.startActivity(new Activity("com.app1", ".MainActivity")); // 切换到第二个应用 driver.startActivity(new Activity("com.app2", ".MainActivity")); // 返回第一个应用 driver.activateApp("com.app1");混合应用测试需要处理WebView:
// 获取所有context Set<String> contexts = driver.getContextHandles(); // 切换到WEBVIEW driver.context("WEBVIEW_com.example.app"); // 现在可以用Selenium方式定位web元素 driver.findElement(By.cssSelector(".web-button")).click(); // 切换回原生context driver.context("NATIVE_APP");性能测试集成:
// 启动性能数据收集 driver.executeScript("mobile: startPerfRecord", ImmutableMap.of( "profileName", "cpu_memory", "timeout", 60000 )); // 执行测试操作... // 停止收集并获取数据 String perfData = (String) driver.executeScript("mobile: stopPerfRecord");安全测试方案:
// 检查应用是否可调试 String debuggable = driver.executeScript("mobile: shell", ImmutableMap.of( "command", "getprop ro.debuggable" )).toString(); Assert.assertEquals(debuggable.trim(), "0", "应用不应该可调试"); // 检查日志中敏感信息 driver.manage().logs().get("logcat").forEach(entry -> { Assert.assertFalse(entry.getMessage().contains("password"), "日志中包含敏感信息"); });跨平台测试策略:
// 通用测试基类 public abstract class CrossPlatformTest { protected abstract AppiumDriver createDriver(); protected abstract void installApp(); @Test public void testLogin() { // 通用测试逻辑 } } // Android实现 public class AndroidLoginTest extends CrossPlatformTest { protected AppiumDriver createDriver() { // 返回Android驱动 } } // iOS实现 public class iOSLoginTest extends CrossPlatformTest { protected AppiumDriver createDriver() { // 返回iOS驱动 } }