Android自动化测试与CI/CD实战
·
Android自动化测试与CI/CD实战
阅读时间: 23分钟
适合人群: Android开发者
你将收获: 掌握CI/CD流程,实现自动化部署
一个因手动打包引发的线上事故
2021年双11前夜,我们紧急修复了一个严重Bug。
晚上11点:开发完成,准备发版。
打包过程:
# 开发A:打包
./gradlew assembleRelease
# 开发B:签名
jarsigner -verbose -keystore release.jks app-release.apk
# 开发C:上传Google Play
# 手动上传...
# 开发D:更新版本号
# 手动修改build.gradle...
凌晨1点:上线完成,团队下班。
凌晨2点:用户投诉暴增。
问题诊断:
开发C上传了错误的APK文件
→ 上传的是旧版本!
→ Bug依然存在!
→ 双11当天崩溃率15%!
紧急回滚,但损失已造成:
损失统计:
- 销售额损失:100万
- 用户投诉:5000+条
- 应用评分:
- 团队通宵加班:10人
复盘:
根本原因:
1. 手动打包容易出错
2. 没有自动化流程
3. 没有版本控制
4. 没有测试验证
决定:
引入CI/CD自动化流程
实施CI/CD后:
自动化流程:
1. 代码提交 → 自动触发CI
2. 自动运行测试
3. 测试通过 → 自动打包
4. 自动签名
5. 自动上传到分发平台
6. 自动通知团队
效果:
- 发版时间:2小时 → 15分钟
- 出错概率:30% → 0%
- 团队效率:
这次事故让我明白:CI/CD是现代开发的标配。
什么是CI/CD?
CI (Continuous Integration)
持续集成:
- 自动编译
- 自动测试
- 及时发现问题
CD (Continuous Delivery/Deployment)
持续交付/部署:
- 自动打包
- 自动发布
- 快速交付
CI/CD工具选择
常用CI/CD平台
平台 特点 适用场景
────────────────────────────────────────────────────
GitHub Actions 免费、集成好 开源项目
GitLab CI 功能强大、自托管 企业项目
Jenkins 老牌、插件多 复杂项目
CircleCI 云端、配置简单 中小项目
Travis CI 配置简单 开源项目
GitHub Actions实战
1. 基础工作流
# .github/workflows/android.yml
name: Android CI
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout代码
uses: actions/checkout@v3
- name: 设置JDK
uses: actions/setup-java@v3
with:
java-version: '17'
distribution: 'temurin'
- name: 授予Gradle执行权限
run: chmod +x gradlew
- name: 构建APK
run: ./gradlew assembleDebug
- name: 上传APK
uses: actions/upload-artifact@v3
with:
name: app-debug
path: app/build/outputs/apk/debug/app-debug.apk
2. 运行单元测试
name: Android Test
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: 设置JDK
uses: actions/setup-java@v3
with:
java-version: '17'
distribution: 'temurin'
- name: 运行单元测试
run: ./gradlew test
- name: 上传测试报告
uses: actions/upload-artifact@v3
if: failure()
with:
name: test-reports
path: app/build/reports/tests/
- name: 生成测试覆盖率
run: ./gradlew jacocoTestReport
- name: 上传覆盖率报告
uses: codecov/codecov-action@v3
with:
files: ./app/build/reports/jacoco/jacocoTestReport/jacocoTestReport.xml
3. 自动发布
name: Release
on:
push:
tags:
- 'v*' # 推送tag时触发
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: 设置JDK
uses: actions/setup-java@v3
with:
java-version: '17'
distribution: 'temurin'
- name: 解码Keystore
run: |
echo "${{ secrets.KEYSTORE_BASE64 }}" | base64 -d > release.jks
- name: 构建Release APK
run: ./gradlew assembleRelease
env:
KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}
KEY_ALIAS: ${{ secrets.KEY_ALIAS }}
KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}
- name: 创建Release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ github.ref }}
release_name: Release ${{ github.ref }}
draft: false
prerelease: false
- name: 上传Release APK
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./app/build/outputs/apk/release/app-release.apk
asset_name: app-release.apk
asset_content_type: application/vnd.android.package-archive
GitLab CI实战
完整Pipeline
# .gitlab-ci.yml
stages:
- build
- test
- deploy
variables:
ANDROID_COMPILE_SDK: "33"
ANDROID_BUILD_TOOLS: "33.0.0"
before_script:
- export GRADLE_USER_HOME=`pwd`/.gradle
- chmod +x ./gradlew
cache:
paths:
- .gradle/wrapper
- .gradle/caches
build_debug:
stage: build
script:
- ./gradlew assembleDebug
artifacts:
paths:
- app/build/outputs/apk/debug/app-debug.apk
expire_in: 1 week
build_release:
stage: build
only:
- tags
script:
- ./gradlew assembleRelease
artifacts:
paths:
- app/build/outputs/apk/release/app-release.apk
expire_in: 1 month
unit_test:
stage: test
script:
- ./gradlew test
artifacts:
when: always
reports:
junit: app/build/test-results/test*/TEST-*.xml
lint_check:
stage: test
script:
- ./gradlew lintDebug
artifacts:
paths:
- app/build/reports/lint-results-debug.html
when: always
deploy_internal:
stage: deploy
only:
- develop
script:
- ./gradlew assembleDebug
- |
curl -F "file=@app/build/outputs/apk/debug/app-debug.apk" \
-F "token=$FIREBASE_TOKEN" \
https://upload.firebase.com/upload
environment:
name: internal
url: https://appdistribution.firebase.com
deploy_production:
stage: deploy
only:
- tags
script:
- ./gradlew assembleRelease
- ./gradlew publishReleaseBundle
environment:
name: production
url: https://play.google.com
自动化签名
1. Keystore配置
// build.gradle (Module: app)
android {
signingConfigs {
release {
storeFile file(System.getenv("KEYSTORE_FILE") ?: "release.jks")
storePassword System.getenv("KEYSTORE_PASSWORD")
keyAlias System.getenv("KEY_ALIAS")
keyPassword System.getenv("KEY_PASSWORD")
}
}
buildTypes {
release {
signingConfig signingConfigs.release
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}
2. CI环境变量
# GitHub Secrets
KEYSTORE_BASE64: (base64编码的keystore文件)
KEYSTORE_PASSWORD: your_store_password
KEY_ALIAS: your_key_alias
KEY_PASSWORD: your_key_password
# 生成base64编码
base64 -i release.jks | pbcopy
自动化测试
1. UI测试(Espresso)
@RunWith(AndroidJUnit4::class)
class MainActivityTest {
@get:Rule
val activityRule = ActivityScenarioRule(MainActivity::class.java)
@Test
fun testLoginFlow() {
// 输入用户名
onView(withId(R.id.username))
.perform(typeText("testuser"), closeSoftKeyboard())
// 输入密码
onView(withId(R.id.password))
.perform(typeText("password"), closeSoftKeyboard())
// 点击登录
onView(withId(R.id.login_button))
.perform(click())
// 验证跳转
onView(withId(R.id.welcome_text))
.check(matches(isDisplayed()))
}
}
2. CI中运行UI测试
ui_test:
stage: test
script:
- ./gradlew connectedDebugAndroidTest
artifacts:
paths:
- app/build/reports/androidTests/connected/
when: always
自动化部署
1. Firebase App Distribution
// build.gradle (Project)
buildscript {
dependencies {
classpath 'com.google.firebase:firebase-appdistribution-gradle:4.0.0'
}
}
// build.gradle (Module: app)
plugins {
id 'com.google.firebase.appdistribution'
}
firebaseAppDistribution {
releaseNotesFile = "release-notes.txt"
testers = "team@company.com, qa@company.com"
}
# CI配置
deploy_firebase:
stage: deploy
script:
- ./gradlew assembleDebug appDistributionUploadDebug
2. Google Play Console
// build.gradle (Project)
buildscript {
dependencies {
classpath 'com.github.triplet.gradle:play-publisher:3.8.4'
}
}
// build.gradle (Module: app)
plugins {
id 'com.github.triplet.play'
}
play {
serviceAccountCredentials.set(file("service-account.json"))
track.set("internal") // internal, alpha, beta, production
}
# CI配置
deploy_play_store:
stage: deploy
only:
- tags
script:
- ./gradlew publishReleaseBundle
自动化通知
1. Slack通知
notify_slack:
stage: deploy
when: always
script:
- |
curl -X POST -H 'Content-type: application/json' \
--data "{\"text\":\"Build $CI_PIPELINE_ID finished with status: $CI_JOB_STATUS\"}" \
$SLACK_WEBHOOK_URL
2. 钉钉通知
notify_dingtalk:
stage: deploy
when: always
script:
- |
curl -X POST \
"$DINGTALK_WEBHOOK" \
-H 'Content-Type: application/json' \
-d "{
\"msgtype\": \"text\",
\"text\": {
\"content\": \"构建完成:$CI_PIPELINE_ID\"
}
}"
完整的CI/CD流程
最佳实践
1. 分支策略
- main: 生产环境
- develop: 开发环境
- feature/*: 功能分支
2. 自动化测试
- 单元测试覆盖率 > 60%
- UI测试覆盖关键流程
- 每次提交都运行测试
3. 代码审查
- 强制Code Review
- CI检查通过才能合并
- 至少2人审查
4. 版本管理
- 使用语义化版本(1.2.3)
- Tag触发发布
- 自动生成Changelog
5. 通知机制
- 构建失败立即通知
- 发布成功通知团队
- 定期报告构建统计
总结
CI/CD是现代Android开发的基础设施。
核心价值:
1. 提升效率
- 自动化构建
- 快速反馈
- 减少人工操作
2. 保证质量
- 自动化测试
- 代码检查
- 及时发现问题
3. 降低风险
- 标准化流程
- 版本可追溯
- 快速回滚
4. 团队协作
- 统一流程
- 透明可见
- 提升士气
记住:
CI/CD不是奢侈品
而是现代开发的必需品
自动化 = 效率 + 质量
当你掌握了CI/CD,开发将更加高效和可靠。
下一篇预告:《Android开发最佳实践总结》
作者: Andy Gong
专注: Android自动化与DevOps
#Android #CICD #Automation #DevOps #GitHubActions #GitLabCI
更多推荐



所有评论(0)