器→工具, 工具软件, 术→技巧, 研发

移动端自动化测试工具Appium

钱魏Way · · 6 次浏览

在移动互联网时代,Android和iOS应用的测试变得越来越重要。为了保证应用的质量和用户体验,自动化测试成为了必不可少的一环。Appium作为一个开源的移动端自动化测试框架,为开发者和测试工程师提供了强大的工具来自动化测试Android和iOS应用。本文将详细介绍Appium的核心概念、安装配置、使用方法以及实战案例,帮助读者全面掌握这一主流移动测试工具。

Appium核心概念与设计理念

什么是Appium?

Appium是一个开源的、跨平台的移动应用自动化测试框架。它允许开发者和测试人员使用多种编程语言(如Java、Python、C#、Ruby、JavaScript等)编写测试脚本,来自动化测试原生应用(Native Apps)、混合应用(Hybrid Apps)和Web应用(Web Apps)。

Appium的核心思想是,它并不直接运行在设备上,而是作为客户端运行在你的电脑上,通过与移动设备上的服务端(如Android的UiAutomator或iOS的XCTest)通信,来控制应用。这种架构使得Appium能够支持多种编程语言,并且与原生应用的交互方式非常接近。

Appium的设计理念

Appium旨在满足移动端自动化需求的理念,概述为以下四个原则:

  • 不修改应用:不应该为了自动化而重新编译你的应用或以任何方式修改它
  • 语言无关性:不应该被限制在特定的语言或框架上来编写运行测试
  • 不重复造轮子:移动端自动化框架不应该在自动化接口方面重造轮子
  • 开源精神:移动端自动化框架应该开源,在精神、实践以及名义上都该如此

Appium的架构与运行原理

Appium采用客户端/服务器架构,核心是暴露REST API的网络服务器。它接受来自客户端的连接,监听命令并在移动设备上执行,答复表示执行结果的HTTP响应。

运行原理如下:

  • 客户端发送请求:客户端运行脚本时,调用Appium API会向Appium Server端发送HTTP请求,请求内容是根据WebDriver协议规定的JSON格式数据
  • 服务器转发指令:Appium Server端接收到请求后,解析出JSON数据并发送到手机端
  • 设备端监听处理:手机端上由jar(iOS为BootStrip.js)开启的socket服务器监听相应端口,将请求翻译成UIAutomator能执行的命令
  • 执行操作:通过UIAutomator处理并操作APP完成测试

Appium环境安装与配置

基础依赖安装

安装Node.js和npm

Appium服务器是基于Node.js构建的,因此首先需要安装Node.js和npm。可以从Node.js官网下载安装,安装后通过命令行运行node –version和npm –version验证安装成功。

安装Appium Server

可以通过npm全局安装Appium Server:

npm install -g appium

如果网络较慢,可以使用国内镜像加速安装:

npm install -g cnpm --registry= https://registry.npm.taobao.org
cnpm install -g appium

安装Appium Desktop(可选但推荐)

Appium Desktop是Appium Server的图形界面包装器,它打包了Appium服务器运行需要的所有东西,还提供一个Inspector工具可以查看应用层级结构。可以从GitHub的Releases页面下载最新版本。

Android环境配置

安装Android SDK

要测试Android应用,需要安装Android SDK和相关工具。推荐直接安装Android Studio,然后通过SDK Manager下载所需的SDK版本。

配置环境变量

安装后需要设置环境变量:

  • ANDROID_HOME:指向Android SDK的根目录
  • PATH:添加%ANDROID_HOME%\platform-tools和%ANDROID_HOME%\tools到系统PATH

安装Java JDK

需要安装Java JDK并配置环境变量,确保java -version命令可以正常执行。

iOS环境配置(Mac专属)

Appium驱动iOS设备必须要在Mac下进行,Windows和Linux平台无法完成。需要配置:

  • macOS 10.12及更高版本
  • XCode 8及更高版本
  • 执行xcode-select –install命令安装开发依赖

验证安装

可以使用appium-doctor命令来确认安装Appium所需的依赖是否都已满足要求:

npm install -g appium-doctor
appium-doctor

可以搭配–ios或–android选项检查特定平台的环境。

Appium客户端与项目配置

客户端程序库选择

Appium支持多种编程语言的客户端程序库,包括Java、Python、Ruby、PHP、JavaScript和C#等。这些客户端支持Appium对WebDriver协议的扩展,需要用来代替通常的WebDriver客户端。

Java项目配置示例

对于Java项目,可以使用Maven管理依赖。在pom.xml中添加必要的依赖项:

<dependencies>
    <!-- Appium Java Client -->
    <dependency>
        <groupId>io.appium</groupId>
        <artifactId>java-client</artifactId>
        <version>9.0.0</version>
    </dependency>
    
    <!-- Selenium WebDriver (Appium依赖) -->
    <dependency>
        <groupId>org.seleniumhq.selenium</groupId>
        <artifactId>selenium-java</artifactId>
        <version>4.15.0</version>
    </dependency>
    
    <!-- TestNG (用于测试框架) -->
    <dependency>
        <groupId>org.testng</groupId>
        <artifactId>testng</artifactId>
        <version>7.8.0</version>
        <scope>test</scope>
    </dependency>
</dependencies>

Python项目配置示例

对于Python项目,可以使用pip安装Appium客户端:

pip install appium-python-client

Desired Capabilities配置与驱动初始化

Desired Capabilities是一些发送给Appium服务器的键值对集合,告诉服务器我们想要启动什么类型的自动化会话。它定义了设备平台、版本、APP信息等关键参数。

关键Capabilities包括:

  • platformName:指定测试平台,如”Android”或”iOS”
  • deviceName:指定要连接的设备名称,可以是真机或模拟器的名称
  • appPackage(Android)/ bundleId(iOS):应用的包名
  • appActivity(Android):应用启动时的主Activity名称
  • automationName:指定使用的自动化引擎,Android常用”UiAutomator2″,iOS用”XCUITest”
  • noReset:设置为true可以避免每次启动应用时重置应用数据和设置
  • fullReset:设置为true会在每次启动时完全卸载并重新安装应用

获取APP参数

获取appPackage和appActivity

对于Android应用,可以通过以下方法获取:

  • 直接询问开发:如果被测对象是自研的APP,直接问开发同学最省时省力
  • 使用adb命令:
adb shell "dumpsys window | grep mCurrentFocus"
  • 使用monkey命令:
adb shell monkey -p com.xxxxxxx -vvv 1
  • 查看logcat日志:
adb logcat>E:/app_log.txt

然后在日志文件中搜索”Displayed”关键字

获取deviceName

通过adb devices命令可以查看连接的设备ID,device id可以在手机的【设置】中的【状态消息】查到。

驱动初始化示例(Java)

import io.appium.java_client.AppiumDriver;
import io.appium.java_client.android.AndroidDriver;
import org.openqa.selenium.remote.DesiredCapabilities;
import java.net.URL;
import java.time.Duration;

public class DriverManager {
    private static AppiumDriver driver;
    
    public static AppiumDriver getDriver() {
        if (driver == null) {
            try {
                DesiredCapabilities capabilities = new DesiredCapabilities();
                capabilities.setCapability("platformName", "Android");
                capabilities.setCapability("deviceName", "emulator-5554");
                capabilities.setCapability("appPackage", "com.example.myapp");
                capabilities.setCapability("appActivity", "com.example.myapp.MainActivity");
                capabilities.setCapability("automationName", "UiAutomator2");
                capabilities.setCapability("noReset", true);
                
                driver = new AndroidDriver(new URL(" http://localhost:4723/wd/hub "), capabilities);
                driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(10));
            } catch (Exception e) {
                throw new RuntimeException("Failed to create Appium driver", e);
            }
        }
        return driver;
    }
}

元素定位与操作

常用定位方式

Appium支持Selenium的大部分By定位方式,同时也有移动端特有的定位方式:

通用定位方式

  • id():根据resource-id定位(Android)
  • className():根据类名定位
  • xpath():使用XPath定位
  • cssSelector():使用CSS选择器定位

Android特有定位方式

  • accessibilityId():用于定位content-desc属性,这是Android推荐的定位方式之一
WebElement signInButton = driver.findElement(By.accessibilityId("Sign In"));
  • androidUIAutomator():使用UiAutomator的字符串来定位
WebElement helloWorldText = driver.findElement(By.androidUIAutomator("new UiSelector().text(\"Hello World\")"));

iOS特有定位方式

  • accessibilityId():同样适用于iOS,定位accessibility identifier
  • classChain():iOS特有的链式定位
  • predicate():使用NSPredicate表达式定位

元素操作

常见的元素操作包括:

  • 点击操作:click()
  • 输入文本:sendKeys(“text”)
  • 清除文本:clear()
  • 获取文本:getText()
  • 获取属性:getAttribute(“attributeName”)

等待机制

移动端应用加载较慢,需要合理使用等待机制:

  • 隐式等待:设置全局等待时间
driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(10));
  • 显式等待:针对特定条件等待
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(15));
WebElement element = wait.until(ExpectedConditions.presenceOfElementLocated(By.id("elementId")));

Appium Inspector使用

启动Inspector

确保Appium Server已经启动并且连接手机后,可以启动Appium Inspector。一般通过Appium Desktop启动,也可以从官网单独安装Inspector。

配置Desired Capabilities

在Inspector中配置Desired Capabilities:

  • 点击右边的加号添加属性
  • 填入获取的各项参数(platformName、deviceName、appPackage、appActivity等)
  • 填写完成后会出现JSON格式的表示信息
  • 点击”Start Session”启动APP

Inspector界面功能

Inspector提供多种功能:

  • Native App Mode:切换为原生APP模式
  • Web/Hybrid App Mode:切换为混合APP模式
  • Select Elements:选择元素模式,类似于浏览器调试模式
  • Swipe By Coordinates:使用坐标滑动
  • Tap By Coordinates:使用坐标点击
  • Start Recording:开始录制,自动将操作转换成代码
  • Search for element:根据定位条件搜索元素

实战案例:Android应用自动化测试

案例场景

以抖音极速版为例,实现以下自动化测试场景:

  • 启动Android模拟器/真实设备上的抖音极速版
  • 点击搜索框
  • 输入”软件测试”并搜索
  • 校验搜索结果存在
  • 截图留存

Python实现代码

from appium import webdriver
from appium.webdriver.common.appiumby import AppiumBy
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import time

# Desired Capabilities配置
desired_caps = {
    "platformName": "Android",
    "deviceName": "Pixel 7",  # 设备名称,可通过adb devices -l查看
    "appPackage": "com.ss.android.auto",  # 抖音极速版包名
    "appActivity": ".main.MainActivity",  # 启动Activity
    "automationName": "UiAutomator2",  # Android自动化引擎
    "noReset": True,  # 不重置APP状态
    "unicodeKeyboard": True,  # 支持中文输入
    "resetKeyboard": True  # 测试结束后重置键盘
}

def test_douyin_search():
    # 连接Appium Server
    driver = webdriver.Remote(" http://127.0.0.1:4723/wd/hub ", desired_caps)
    wait = WebDriverWait(driver, 15)  # 显式等待,超时15秒
    
    try:
        # 等待搜索图标加载(Accessibility ID定位)
        search_icon = wait.until(
            EC.presence_of_element_located((AppiumBy.ACCESSIBILITY_ID, "搜索"))
        )
        search_icon.click()
        
        # 等待搜索输入框加载
        search_input = wait.until(
            EC.presence_of_element_located((AppiumBy.ID, "搜索输入框ID"))
        )
        search_input.send_keys("软件测试")
        
        # 点击搜索按钮
        search_btn = driver.find_element(AppiumBy.ACCESSIBILITY_ID, "搜索按钮")
        search_btn.click()
        
        # 验证搜索结果
        time.sleep(2)  # 等待搜索结果加载
        results = driver.find_elements(AppiumBy.CLASS_NAME, "搜索结果项类名")
        assert len(results) > 0, "未找到搜索结果"
        
        # 截图保存
        driver.save_screenshot("search_results.png")
        print("测试成功!搜索结果截图已保存")
        
    finally:
        driver.quit()

Java实现代码

import io.appium.java_client.AppiumDriver;
import io.appium.java_client.android.AndroidDriver;
import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
import java.net.URL;
import java.time.Duration;

public class DouyinTest {
    public static void main(String[] args) {
        AppiumDriver driver = null;
        try {
            DesiredCapabilities caps = new DesiredCapabilities();
            caps.setCapability("platformName", "Android");
            caps.setCapability("deviceName", "emulator-5554");
            caps.setCapability("appPackage", "com.ss.android.auto");
            caps.setCapability("appActivity", ".main.MainActivity");
            caps.setCapability("automationName", "UiAutomator2");
            caps.setCapability("noReset", true);
            
            driver = new AndroidDriver(new URL(" http://localhost:4723/wd/hub "), caps);
            WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(15));
            
            // 定位并点击搜索图标
            WebElement searchIcon = wait.until(
                ExpectedConditions.presenceOfElementLocated(By.accessibilityId("搜索"))
            );
            searchIcon.click();
            
            // 定位搜索输入框并输入文本
            WebElement searchInput = wait.until(
                ExpectedConditions.presenceOfElementLocated(By.id("搜索输入框ID"))
            );
            searchInput.sendKeys("软件测试");
            
            // 点击搜索按钮
            WebElement searchBtn = driver.findElement(By.accessibilityId("搜索按钮"));
            searchBtn.click();
            
            // 验证结果
            Thread.sleep(2000);
            List<WebElement> results = driver.findElements(By.className("搜索结果项类名"));
            assert results.size() > 0 : "未找到搜索结果";
            
            // 截图
            File screenshot = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE);
            FileUtils.copyFile(screenshot, new File("search_results.png"));
            
            System.out.println("测试成功完成!");
            
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (driver != null) {
                driver.quit();
            }
        }
    }
}

最佳实践与常见问题

最佳实践建议

  • 使用Page Object模式:将页面元素和操作封装成类,提高代码可维护性
  • 优先使用稳定的定位方式:推荐使用accessibilityId,避免使用易变的XPath绝对路径
  • 合理使用等待机制:移动端加载较慢,需要充足的等待时间
  • 配置合适的Capabilities:根据测试需求设置noReset、fullReset等参数
  • 日志记录与错误处理:添加详细的日志记录和健壮的错误处理机制
  • 截图与视频录制:关键步骤截图,复杂流程可以考虑录制视频

常见问题解决

  • 设备连接失败:检查USB调试是否开启,驱动是否安装正确
  • Appium Server启动失败:检查端口是否被占用,js版本是否兼容
  • 元素定位失败:使用Appium Inspector确认元素属性,尝试不同的定位方式
  • 中文输入问题:设置unicodeKeyboard和resetKeyboard能力
  • 应用闪退:检查应用兼容性,尝试不同的automationName

持续集成集成

Appium可以轻松集成到CI/CD流程中,常见的集成方式包括:

  • 与Jenkins集成,定时执行自动化测试
  • 与GitLab CI/CD集成,代码提交后自动测试
  • 使用Docker容器化测试环境
  • 集成测试报告生成工具(Allure、ExtentReports等)

总结与展望

Appium作为移动端自动化测试的主流工具,凭借其跨平台、多语言支持、开源等优势,已经成为移动测试领域的重要选择。通过本文的详细介绍,读者可以掌握Appium从环境搭建到实战应用的全流程。

随着移动应用技术的不断发展,Appium也在持续演进。未来趋势包括:

  • AI增强测试:结合AI技术实现智能元素定位、测试用例生成
  • 云测试平台集成:更好地与云测试平台(如Sauce Labs、BrowserStack)集成
  • 性能测试扩展:集成性能监控能力,实现功能与性能一体化测试
  • 跨端测试统一:进一步统一Android、iOS、Web端的测试体验

无论你是刚入门的新手,还是正在进阶的开发者,掌握Appium都将为你的移动测试工作带来显著效率提升。希望本文能为你提供实用的参考和启发,助力你在移动自动化测试领域稳健成长。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注