Appium+Java自动化测试实战:从环境搭建到企业级应用

Appium+Java自动化测试实战:从环境搭建到企业级应用 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 groupIdio.appium/groupId artifactIdjava-client/artifactId version9.4.0/version /dependency !-- TestNG测试框架 -- dependency groupIdorg.testng/groupId artifactIdtestng/artifactId version7.8.0/version scopetest/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测试类继承BaseTestpublic 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 nameParallel Tests paralleltests thread-count3 test nameAndroid Test classes class namecom.example.AndroidTests/ /classes /test test nameiOS Test classes class namecom.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 -Dplatformandroid -Ddeviceemulator生成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 groupIdio.qameta.allure/groupId artifactIdallure-maven/artifactId version2.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 SetString 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驱动 } }