移动应用测试自动化的实战手记:从零到一的旅程

#唐志华
2025-06-18 22:09
阅读 615

开篇:为什么选择这条路?

开篇:为什么选择这条路?

大家好,我是张浩,一名在一线移动开发岗位摸爬滚打多年的老兵。今天想和大家分享一下我过去两年参与并主导的一个项目——移动应用自动化测试体系建设的全过程。

这个项目不是我一开始就主动“选”的,而是被逼出来的。随着我们产品功能不断迭代、业务逻辑越来越复杂、团队人数逐渐扩大,传统的“人工点击”测试方式已经明显跟不上节奏了。每天上线前靠 QA 手动覆盖几百个用例几乎变成了灾难:漏测频繁、效率低、人力成本高。我们急需一套稳定的测试自动化方案,帮助我们在快速交付的同时守住质量关。

这篇文章记录的就是我在落地过程中遇到的实际问题、踩过的坑、以及最后总结出的一套适合中小团队落地的解决方案。

问题描述:当需求变快,质量开始失控

原生应用架构-1

问题描述:当需求变快,质量开始失控

2022年中旬,我们公司内部启动了一个新的 APP 产品线,目标是在3个月内完成 MVP(最小可行产品)并上线各大应用市场。初期为了赶进度,采用的是敏捷开发+手动回归的方式。但很快我们就发现:

  • 版本更新频繁:平均每周要发2个版本
  • 核心流程回归压力大:每次新版本都需要覆盖几十个关键路径,比如登录、下单、支付等
  • QA 团队资源有限:只有一个 QA,负责 Android 和 iOS 两端测试
  • 兼容性测试覆盖率低:不同设备、系统版本组合太多,根本无法全量测试
  • 回归稳定性差:人为操作带来的不确定性高,同一个测试流程每次结果可能不一样

更严重的是,有一回发布时因为一个登录后的引导页文案没显示出来,虽然不影响功能,但在 iOS App Store 审核中被打回,白白浪费了几天时间。这种“不该出的错”,一次次敲打着我们的神经。

我们必须找到一种稳定、高效、可重复执行的测试手段来应对这些问题。


解决方案:如何构建移动测试自动化框架?

解决方案:如何构建移动测试自动化框架?

明确目标

我们当时的目标很清晰:

  1. 实现关键路径的 UI 自动化,用于每日构建后自动回归验证
  2. 支持 Android 和 iOS 双平台,便于统一维护
  3. 尽可能降低测试脚本编写的门槛,方便 QA 协作开发
  4. 快速集成进 CI/CD 流水线,实现自动化运行

技术选型对比

我们调研了几种主流方案,并做了简单比较:

方案名称 优点 缺点 适用场景
Appium + WebDriverAgent 开源,支持双平台;社区活跃;兼容性强 稳定性略差,定位元素性能一般 中小型项目
Espresso(仅 Android) 性能好,Google 原生支持 不支持 iOS,扩展性较弱 专注 Android 项目
XCTest(仅 iOS) 原生支持,性能优越 与 Android 体系割裂 纯 iOS 应用
Detox(React Native 专用) 原生级测试,不依赖 WebView 仅限 RN 框架使用 React Native 项目
XCUITest + UI Automator2 组合使用 各自成熟稳定 管理成本高 大型复杂项目
WebdriverIO + Appium + BrowserStack 支持云端真实设备集群 成本较高、配置复杂 需大规模兼容测试

最终,我们选择了 Appium + WebDriverAgent + Java(TestNG 框架) 作为主要技术栈。

之所以这样选,是因为:

  1. 我们的产品虽然是原生写的,但后期有向 React Native 过渡的规划;
  2. QA 队伍有一定的编码基础,Java 更容易上手;
  3. Appium 的生态比较成熟,文档丰富,遇到问题解决起来相对轻松;
  4. 公司内部有部分已有接口测试是 Java 写的,有利于整合;
  5. 另外,我们也预留了一部分预算,打算后续接入云真机平台。

实践过程:从搭建环境到跑起第一个脚本

跨平台开发对比-2

实践过程:从搭建环境到跑起第一个脚本

一、环境准备

我们需要搭建一个跨平台可用的测试环境,具体包括:

  • Mac + iPhone(本地模拟器 & 真机)
  • PC + Android 模拟器/真机
  • Jenkins 构建服务器(CI)
  • Node.js + Appium Server
  • Java + TestNG + Maven 项目结构

先说个题外话:Appium 对环境的要求有点“娇气”。特别是 iOS 这边,安装 WebDriverAgent 时就遇到了签名错误、bundle ID 冲突等问题。折腾了好几次才搞定。

后来我们做了一个标准化的 Docker 容器镜像,将 Appium、Node、Java、Xcode 工具链打包进去,供所有成员复用。这一步极大降低了新同学入门的门槛。

二、工程结构设计

我们采用的是 Maven 结构,目录如下:

/src/
├── test/
│   ├── java/               # 测试代码
│   │   ├── pageobjects/      # 页面模型
│   │   ├── testsuite/        # 测试用例
│   │   └── utils/            # 公共方法
│   └── resources/            # 配置文件
├── pom.xml                   # Maven 依赖管理

这样做的好处是可以清晰划分模块,也便于后期扩展多端适配。

三、UI 层抽象:Page Object 模式

这是我认为整个自动化中最核心的设计模式。举个例子,我们有一个“商品详情页”,定义成一个类 ProductDetailPage

public class ProductDetailPage {
    private AppiumDriver driver;

    public ProductDetailPage(AppiumDriver driver) {
        this.driver = driver;
    }

    public void clickAddToCart() {
        driver.findElement(By.id("product_detail_add_to_cart")).click();
    }

    public boolean isPriceDisplayed() {
        return driver.findElement(By.id("product_price")).isDisplayed();
    }
}

在测试类中调用:

@Test
public void addToCartSuccessTest() {
    new ProductDetailPage(driver).clickAddToCart();
    Assert.assertTrue(new CartPage(driver).isItemInCart());
}

这样分离了页面逻辑和测试逻辑,大大提升了可维护性。即使产品经理天天改 UI,我们只需要修改对应的 Page 文件即可,不需要每一处都去调整。


踩过的主要坑和经验分享

坑1:iOS 上元素找不到,经常超时

一开始,我们用的都是 findElement(By.id("xxx")),但在 iOS 上有时候会查不到,或者查找速度非常慢。

尝试了很多种策略,最终找到了两个改进点:

  • 使用 XCUIElementType 定位策略,例如通过 AccessibilityId:

    By.xpath("//XCUIElementTypeButton[@name='立即购买']");
    
  • 设置全局等待时间(隐式等待 + 显式等待结合):

    WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));
    wait.until(ExpectedConditions.visibilityOfElementLocated(By.id("login_success")));
    

建议做法:优先为需要自动化的 UI 控件添加 accessibilityIdentifier 字段(iOS),Android 上使用 content-desc 或者 resource-id。

坑2:iOS 设备连接不上 / 权限问题

这个问题特别折磨人。有时候连着电脑没问题,断开之后再接回来就一直提示“请信任此电脑”。

我们最后找到了一个折中方案:

  • 在测试脚本中自动调用命令行工具如 ios-deploylibimobiledevice 来进行预检查;

  • 每次重新连接设备之前清理一次缓存:

    idevicesyslog -u [deviceId] --clear
    

当然最直接的办法还是——给 QA 准备一台固定机器,设备长期插着别拔。

坑3:Appium 不稳定 / 重启失败

有些时候,Appium Server 会出现莫名卡住的情况,导致整个 CI 构建失败。

我们后来引入了健康检测机制,在每条测试用例之前加入心跳检测:

// 判断 App 是否还在前台
boolean checkAppIsActive() {
    return "com.my.app".equals(driver.getCurrentPackage());
}

如果检测失败,脚本会尝试重新启动 App:

driver.activateApp(bundleId); // iOS
driver.activateApp(packageName); // Android

这部分逻辑可以通过封装成通用库函数,让每个测试用例都能共享。


效果总结:上线后的变化

经过三个月的努力,我们终于建立了一套较为完整的测试自动化流程,效果显著:

  • 每日构建自动化测试率提升到 75%+,关键路径实现全覆盖;
  • 上线前 Bug 下降约 40%,尤其是回归类缺陷大幅减少;
  • QA 释放了大量时间,转而去探索更多边界 case 和探索性测试;
  • 集成 Jenkins 后,报告可视化程度大大提高,每天早上邮件发送测试结果;
  • 配合 Appium + Docker + Selenium Grid,初步实现分布式执行
  • 后续对接上了阿里云的 MCloud 真机云测平台,进一步提高设备兼容性覆盖面

值得一提的是,我们有一次发布前,测试自动化发现了隐藏很深的“登录失效跳转错误”问题,避免了上线后的客诉风险。那一刻我觉得这一切努力都值了。


经验分享:写给正在路上的你

如果你也在考虑搭建移动端自动化测试体系,以下是我踩过很多坑之后的一些真诚建议:

✅ 1. 从关键路径入手,不要追求“全自动化”

自动化不是万能的,也不是越全越好。重点照顾那些“高频回归、影响大”的核心流程,比如注册、登录、支付、搜索等。这些往往是最容易出问题的地方。

✅ 2. 提前做好 UI 控件的命名规范

UI 标签尽量加上 resource-id(Android)或 accessibilityIdentifier(iOS),这对自动化测试来说至关重要。不然你会陷入无穷无尽的 xpath 查找地狱。

✅ 3. 尽量使用显式等待代替 Thread.sleep()

这是初学者最容易犯的错误之一。sleep 是不可控的,会导致测试不稳定且耗时冗余。正确的方法是使用 WebDriverWait 配合 ExpectedConditions。

✅ 4. 做好失败日志采集和截图机制

自动化测试一定要有完善的异常捕捉机制。建议:

  • 每次用例失败自动截图
  • 记录当前 Activity 或页面名称
  • 打印详细的日志信息(例如 Appium server 日志、崩溃堆栈)

我们后来把这些都封装成了一个 BaseTest 类,继承后自动处理失败逻辑。

✅ 5. 把自动化纳入 CI/CD 流程

自动化测试的价值在于持续运行。只有把它集成到每天的构建流水中,才能发挥最大效能。

我们使用的是 Jenkins Pipeline:

pipeline {
    agent any
    stages {
        stage('Build App') {
            steps { sh './build.sh' }
        }
        stage('Run Tests') {
            steps { sh 'mvn test' }
        }
        stage('Generate Report') {
            steps { sh 'allure generate target/allure-results' }
        }
    }
}

✅ 6. 探索云端服务,提升覆盖能力

我们前期自己搭真机设备,后来发现管理起来太麻烦。于是转向使用阿里云的 MCloud 和 BrowserStack 的混合方案,既节省设备成本,又能覆盖更多机型和版本。


最后一点感想:测试自动化不是终点,而是起点

说实话,测试自动化从来都不是万金油。它只是一个工具,真正解决问题的,是我们对产品质量的理解和追求。

这两年,我也常常思考一个问题:“自动化到底值不值得投入?”

我的答案是:如果你的产品会长期迭代,那自动化一定值得尽早投入

它不仅仅帮你节约时间、发现 bug,更重要的是帮助你建立起一套可度量、可预测、可持续的质量保障体系

在这条路上,我和团队都成长了不少。也希望这篇带着实战温度的文章,能给你们带来一点点启发。

如果你也有类似的经验、坑、或者疑问,欢迎留言交流~我们一起在质量保障的路上走得更稳、更远。


作者简介:张浩,从业8年的移动开发工程师,目前专注于 Android/iOS 架构设计与测试自动化体系建设。热爱开源与技术分享,公众号“码农的日常”主理人。

评论 0

最热最新
暂无评论
匿名用户Lv.1
0
影响力
0
文章
0
粉丝