28、升级兼容性测试自动化:使用TradeFed搭建自动化测试流水线,CI/CD集成
说实话,做Android系统升级兼容性开发,最怕什么?
我最怕的是:改了一行代码,结果把某个老设备的相机搞崩了。或者,升级了某个底层库,结果第三方应用全挂了。
这种问题,靠人工测,根本测不过来。你想想看,Android碎片化那么严重,不同厂商、不同版本、不同硬件组合,光兼容性测试用例就有几万个。手动跑一遍?那得测到猴年马月去。
所以,自动化测试流水线是必须的。今天我就跟你聊聊,怎么用TradeFed搭建这套流水线,并且集成到CI/CD里。
28.1 TradeFed是什么?为什么选它?
TradeFed,全称是Trade Federation。它是Google官方推出的自动化测试框架。说白了,它就是专门为Android系统测试设计的。
我刚开始接触TradeFed时,也觉得它有点重。但用久了你会发现,它真的很适合做兼容性测试。为什么?
- 原生支持CTS/VTS:Google的兼容性测试套件就是跑在TradeFed上的。你不需要额外适配。
- 设备管理能力强:可以同时管理多台设备,自动分配测试任务。
- 结果解析完善:测试报告是XML格式的,方便后续分析和集成。
- 可扩展性好:你可以写自己的测试模块,插进去就能用。
核心观点:TradeFed不是给你写测试用例的,它是帮你管理和运行测试用例的。你的测试用例可以是JUnit、Python脚本,甚至是一段shell命令。
28.2 搭建TradeFed测试环境
嗯,这里要注意。TradeFed的环境搭建,其实没那么复杂。但有几个坑,我踩过,得跟你说说。
28.2.1 环境准备
你需要准备这些东西:
- 一台Linux服务器(Ubuntu 18.04以上,我推荐20.04)
- Java 8或11(TradeFed依赖Java)
- Android SDK(至少包含adb和fastboot)
- Python 3(用于一些辅助脚本)
我个人习惯用Docker来部署。这样环境隔离,不会污染宿主机。而且换机器时,直接拉镜像就行。
28.2.2 下载和配置TradeFed
TradeFed的源码在AOSP里。你可以直接下载预编译的版本:
# 下载TradeFed
git clone https://android.googlesource.com/platform/tools/tradefederation
# 或者直接下载预编译包
wget https://dl.google.com/android/tradefederation/24.0/tradefederation.zip
unzip tradefederation.zip -d ~/tradefederation
配置环境变量:
export TRADEFED_HOME=~/tradefederation
export PATH=$PATH:$TRADEFED_HOME/bin
启动TradeFed服务:
tradefed.sh start
注意:我曾经遇到过一个问题——启动后连不上设备。后来发现是adb服务没启动。记得先执行 adb start-server。
28.3 编写兼容性测试用例
TradeFed支持多种测试类型。我主要用两种:
- CTS测试:Google官方的兼容性测试。直接跑就行。
- 自定义测试:针对自己项目的特殊需求。
28.3.1 自定义测试用例示例
假设我们要测试升级后,某个系统API是否还能正常工作。可以写一个简单的JUnit测试:
package com.example.compat;
import android.app.ActivityManager;
import android.content.Context;
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
public class UpgradeCompatTest {
@Test
public void testMemoryInfo() {
Context context = InstrumentationRegistry.getTargetContext();
ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
ActivityManager.MemoryInfo info = new ActivityManager.MemoryInfo();
am.getMemoryInfo(info);
// 检查内存信息是否正常
assertTrue("Memory info should not be null", info != null);
assertTrue("Total mem should be > 0", info.totalMem > 0);
}
}
然后,把这个测试打包成APK,放到TradeFed的测试目录下。
28.3.2 配置测试计划
TradeFed用XML文件来定义测试计划。比如:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<test class="com.android.tradefed.testtype.AndroidJUnitTest">
<option name="package" value="com.example.compat" />
<option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
</test>
<device class="com.android.tradefed.device.NativeDevice">
<option name="serial" value="auto" />
</device>
</configuration>
这个配置文件告诉TradeFed:用AndroidJUnitTest来跑测试,设备自动选择。
28.4 搭建自动化流水线
好了,测试用例有了,环境有了。接下来就是搭流水线。
我建议用Jenkins来做CI/CD。为什么?因为Jenkins插件多,社区活跃,而且和TradeFed配合得很好。
28.4.1 流水线整体架构
先看一张图,了解整体流程:
这张图展示了完整的流水线。从代码提交到最终报告,每一步都是自动化的。
28.4.2 Jenkins Pipeline脚本
下面是一个实际的Jenkinsfile示例。我在项目中就是这么用的:
pipeline {
agent any
environment {
TRADEFED_HOME = '/opt/tradefederation'
DEVICE_SERIAL = 'auto'
}
stages {
stage('Checkout') {
steps {
checkout scm
}
}
stage('Build System') {
steps {
sh 'source build/envsetup.sh && lunch aosp_arm64-userdebug && make -j8'
}
}
stage('Flash Device') {
steps {
sh 'fastboot flashall -w'
}
}
stage('Run Compatibility Tests') {
steps {
sh '''
cd $TRADEFED_HOME
./tradefed.sh run commandAndExit \
--template:map test=cts \
--plan cts \
--device-serial $DEVICE_SERIAL \
--skip-preconditions \
--report-dir ${WORKSPACE}/reports
'''
}
}
stage('Parse Results') {
steps {
script {
def result = sh(script: 'python3 parse_cts_results.py ${WORKSPACE}/reports', returnStdout: true).trim()
if (result.contains('FAIL')) {
currentBuild.result = 'UNSTABLE'
}
}
}
}
stage('Generate Report') {
steps {
sh 'python3 generate_html_report.py ${WORKSPACE}/reports ${WORKSPACE}/report.html'
publishHTML(target: [
allowMissing: false,
alwaysLinkToLastBuild: true,
keepAll: true,
reportDir: 'reports',
reportFiles: 'report.html',
reportName: 'Compatibility Test Report'
])
}
}
}
post {
failure {
emailext(
subject: "兼容性测试失败: ${env.JOB_NAME} - ${env.BUILD_NUMBER}",
body: "请检查测试报告: ${env.BUILD_URL}",
to: 'team@example.com'
)
}
}
}
小技巧:我习惯在 Parse Results 阶段加一个阈值判断。比如,失败用例超过5个,就标记为UNSTABLE。超过20个,直接FAIL。这样不会因为一两个小问题就阻断整个流水线。
28.5 集成CI/CD的最佳实践
做了这么多项目,我总结了几条经验:
28.5.1 测试分层
不要把所有测试都放在一个流水线里。我建议分三层:
| 层级 | 测试内容 | 触发时机 | 耗时 |
|---|---|---|---|
| L0 | 单元测试、静态检查 | 每次提交 | <5分钟 |
| L1 | 核心功能兼容性测试 | 每次合并前 | 15-30分钟 |
| L2 | 完整CTS/VTS测试 | 每日构建 | 2-4小时 |
这样做的好处是:快速反馈。L0没过,代码根本进不了仓库。L1没过,合并请求会被阻塞。L2是兜底,确保每日构建的质量。
28.5.2 设备池管理
设备不够用怎么办?我遇到过这个问题。后来用了一个简单的方法:
- 准备一个设备池,比如10台不同型号的手机。
- 用TradeFed的
--device-serial参数指定设备。 - 写一个调度脚本,空闲设备自动分配任务。
嗯,这里要注意。不同设备的兼容性测试结果可能不一样。比如,某台设备的内存小,跑大测试会OOM。所以,我建议在测试计划里,按设备型号分组。
28.5.3 结果可视化
测试报告不能只是堆数据。我习惯用图表展示趋势。比如:
- 通过率折线图:看最近30天的变化。
- 失败用例分布:哪个模块最容易出问题。
- 执行时间趋势:测试是不是越来越慢了。
这些图表,可以用Python的matplotlib生成,然后嵌入到HTML报告里。
避坑指南:我曾经遇到过一个问题——测试结果不稳定。同样的代码,这次跑通过,下次跑就失败。后来发现是设备状态不一致。解决方案是:每次测试前,先执行一次 adb reboot,确保设备处于干净状态。
28.6 总结
好了,今天的内容就这些。总结一下:
- TradeFed是Android兼容性测试的利器,尤其适合大规模自动化。
- 搭建流水线时,注意环境隔离和设备管理。
- CI/CD集成要分层,快速反馈和全面覆盖要兼顾。
- 结果可视化能帮你快速定位问题。
说实话,自动化测试流水线不是一蹴而就的。刚开始可能很简陋,但慢慢迭代,它会成为你项目质量的守护神。
如果你在搭建过程中遇到问题,欢迎交流。毕竟,这些坑我都踩过,能帮你省不少时间。
公众号:蓝海资料掘金营,微信deep3321