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 流水线整体架构

先看一张图,了解整体流程:

TradeFed自动化测试流水线架构 代码提交 Git Push / PR Merge 触发构建 Jenkins Webhook 编译系统 make -j8 刷机部署 fastboot flash 运行测试 TradeFed执行 结果收集 XML解析/归档 报告生成 HTML/邮件通知 通过? 自动合并 回滚/告警

这张图展示了完整的流水线。从代码提交到最终报告,每一步都是自动化的。

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