1. GitLab CI 基础概念
1.1 使用前提
官方指南文档:gitlab.sensorsdata.cn/help/ci/qui…
在开始使用 GitLab CI/CD 之前,需要确保:
执行器 Runner 查看: Settings > CI/CD 然后点击 expand Runners。
1.2 基础概念
1.2.1 安装并注册 GitLab Runner
在 GitLab CI/CD 的构建中,需要 GitLab Runner 执行器用于执行我们的脚本配置。所以首先我们需要创建 Runner。
Homebrew 安装 GitLab Runner
brew ``install gitlab-runner |
|---|
这里就会执行安装程序,一般中途没有错误就表示安装成功了。下一步进行 Runner 注册。
gitlab-runner register |
|---|
在 Runner 注册的过程中,会出现多个输入:
Enter the GitLab instance URL:输入你的GitLab地址Enter the registration token:输入项目下的TokenEnter a description for the runner:设置Runner的描述Enter tags for the runner (comma-separated):设置Runner的Tag标记,这个在后面我们指定执行Runner时有用Enter an executor: virtualbox, docker+machine, docker, parallels, shell, docker-ssh+machine, kubernetes, custom, docker-ssh, ssh:选择执行器,一般可选shell、docker
查看项目对应的 Token 和 GitLab 地址的方式:Project Settings Page > CI/CD > Runner,然后单击展开按钮。
最后完成注册后,我们可以通过
gitlab-runner verify 指令查看 Runner 状态。
删除 Runner
gitlab-runner verify --delete --name runner的标记 |
|---|
1.2.2 镜像 image
image 关键字是 Docker executor 的 Docker 映像的名称,用于运行 CI/CD 作业。默认情况下,Docker 执行器从 Docker Hub 中下载镜像,当然我们也可以指定本地镜像。
使用格式:
image: <image-name> (Same as using <image-name> with the latest tag)
image: <image-name>:<tag>
image: <image-name>@<digest>
示例:
image: runmymind/docker-android-sdk
1.2.3. 关键字
在 job 的脚本中,有以下关键字。
| Keyword | Description |
|---|---|
after_script | Override a set of commands that are executed after job. |
allow_failure | Allow job to fail. A failed job does not cause the pipeline to fail. |
artifacts | List of files and directories to attach to a job on success. |
before_script | Override a set of commands that are executed before job. |
cache | List of files that should be cached between subsequent runs. |
coverage | Code coverage settings for a given job. |
dast_configuration | Use configuration from DAST profiles on a job level. |
dependencies | Restrict which artifacts are passed to a specific job by providing a list of jobs to fetch artifacts from. |
environment | Name of an environment to which the job deploys. |
except | Control when jobs are not created. |
extends | Configuration entries that this job inherits from. |
image | Use Docker images. |
include | Include external YAML files. |
inherit | Select which global defaults all jobs inherit. |
interruptible | Defines if a job can be canceled when made redundant by a newer run. |
needs | Execute jobs earlier than the stage ordering. |
only | Control when jobs are created. |
pages | Upload the result of a job to use with GitLab Pages. |
parallel | How many instances of a job should be run in parallel. |
release | Instructs the runner to generate a release object. |
resource_group | Limit job concurrency. |
retry | When and how many times a job can be auto-retried in case of a failure. |
rules | List of conditions to evaluate and determine selected attributes of a job, and whether or not it's created. |
script | Shell script that is executed by a runner. |
secrets | The CI/CD secrets the job needs. |
services | Use Docker services images. |
stage | Defines a job stage. |
tags | List of tags that are used to select a runner. |
timeout | Define a custom job-level timeout that takes precedence over the project-wide setting. |
trigger | Defines a downstream pipeline trigger. |
variables | Define job variables on a job level. |
when | When to run job. |
1. stages 关键字
用于定义 jobs 任务中的全局执行状态,通常默认的 pipelines 状态包含:.pre、build、test、deploy、.post。
注意在同一个状态的任务脚本并行执行。
2. workflow 关键字
通常配合 rules 关键字用于预设值。
workflow:
rules:
- if: $CI_COMMIT_MESSAGE =~ /-draft$/
when: never
- if: '$CI_PIPELINE_SOURCE == "push"'
3. script 关键字
用于指定需要执行的命令指令。所有的 job 任务都需要这个关键字。
job1:
script: "bundle exec rspec"
job2:
script:
- uname -a
- bundle exec rspec
4. before_script 关键字
用来指定 job 所有的 script 指令执行前应该执行的配置。
job:
before_script:
- echo "Execute this command before any `script:` commands."
script:
- echo "This command executes after the job's `before_script` commands."
5. after_script 关键字
用来指定 job 所有的 script 指令执行后应该执行的配置。
job:
script:
- echo "An example script section."
after_script:
- echo "Execute this command after the `script` section completes."
6. only 关键字
配合内部一些关键字来指定执行的 branch、pilepipe 等。
job1:
script: echo 'test'
job2:
script: echo 'test'
only:
- branches
- tags
7. when 关键字
定义 job 任务执行的时机。
on_success(default): Run the job only when all jobs in earlier stages succeed or haveallow_failure: true.manual: Run the job only when triggered manually.always: Run the job regardless of the status of jobs in earlier stages.on_failure: Run the job only when at least one job in an earlier stage fails.delayed: Delay the execution of a job for a specified duration.never: Don't run the job.
cleanup_build_job:
stage: cleanup_build
script:
- cleanup build when failed
when: on_failure
8. artifacts 关键字
用于指定 job 任务执行成功或失败时的产物文件或文件夹。
build:linux:
stage: build
script: make build:linux
artifacts:
paths:
- binaries/
2. Android 自动化单元测试
2.1 环境配置
在使用 gitlab-ci + docker 配置的环境时,需要使用对应的 android 编译环境镜像。常见的镜像有:
image: jangrewe/gitlab-ci-android
image: runmymind/docker-android-sdk
image: sprheany/docker-android
对应的仓库:
2.1.1 配置 runner 缓存
在服务器上找一个文件夹挂载到 docker 容器里边,给 .gradle 做一个缓存,这样每次编译的时候,就不用一直下载 gradle 了,这里我挂载的是 /home/android-cache 文件夹:
vi /etc/gitlab-runner/config.toml |
|---|
参照文档:blog.csdn.net/Captive_Rai…
2.2. Android 单元测试
Android 中的单元测试可以分为两类:本地单元测试和插桩单元测试。
- 本地测试:运行在本地的计算机上,这些测试编译之后可以直接运行在本地的
Java虚拟机上(JVM)。可以最大限度的缩短执行的时间。如果测试中用到了Android框架中的对象,那么谷歌推荐使用Robolectric来模拟对象。 - 插桩测试:在
Android设备或者模拟器上运行的测试,这些测试可以访问插桩测试信息,比如被测设备的Context,使用此方法可以运行具有复杂Android依赖的单元测试。
本地单元测试是 src/test 文件下的测试用例,插桩测试执行 src/androidTest 文件下的内容。
app/src/androidTest主要是运行在真实手机或者模拟器上的测试,比如集成测试,端到端测试,以及仅靠JVM无法完成的功能验证测试app/src/test在本地计算机上运行的测试,比如单元测试
Android 工程目录:
app/src
├── androidTest/java (仪器化单元测试、UI测试,比如Espresso)
├── main/java (业务代码)
└── test/java (本地单元测试,Junit4、mockito、Robolectric)
这里需要注意对于本地单元测试使用的依赖是 testImplementation,而插桩单元测试使用的依赖是 androidTestImplementation。
关于单元测试的介绍,可以阅读 Android 官方的指南:developer.android.com/training/te…
2.2.1 本地单元测试
本地单元测试用适用于不需要在真实设备上运行测试关联度较低的场景,使用本地单元测试可以更快速来检测我们的代码逻辑。通常使用 junit、Robolectric 框架来实现。
1. 搭建测试环境
在 Android 项目中,本地单元测试的源文件存储目录为:module-name/src/test/java/。一般当我们创建项目时就已经存在了。同时我们也需要为单元测试添加依赖框架,在应用的 build.gradle 文件中添加依赖:
dependencies {
// Required -- JUnit 4 framework
testImplementation 'junit:junit:4.12'
// Optional -- Robolectric environment
testImplementation 'androidx.test:core:1.0.0'
// Optional -- Mockito framework
testImplementation 'org.mockito:mockito-core:1.10.19'
}
2. 创建本地单元测试类
使用 JUnit4 编写测试类,只需要在测试方法前面添加 @Test 注解即可。
import org.hamcrest.MatcherAssert;
import org.junit.Assert;
import org.junit.Test;
public class JUnitTestDemo {
@Test
public void trackEventValid() {
MatcherAssert.assertThat("str1", false);
Assert.assertEquals("str1","st");
}
}
在比较结果时,可以使用 junit.Assert 方法或 Hamcrest 匹配器来完成。
3. 添加框架依赖
在测试的过程汇总,通常会涉及到一些与 Android 系统依赖的工作,这里就需要借助 Robolectric 框架完成,该框架用于在本地 JVM 模拟真实的 Android 框架代码中对象。
import android.content.Context;
import androidx.test.core.app.ApplicationProvider;
import org.junit.Assert;
import org.junit.Test;
public class JUnitTestDemo {
private Context context = ApplicationProvider.getApplicationContext();
@Test
public void trackEventValid() {
String pageName = context.getPackageName();
Assert.assertEquals(pageName, Package.getPackage(pageName).getName());
}
}
2.2.2 插桩单元测试
插桩单元测试是在实体设备或模拟器上运行的测试,这类测试可以更多利用 Android API,测试的保真度比本地单元测试高。Espresso 和 UI Automator 就是这类测试,Espresso 一般用来测试单个界面,UI Automator 一般用来测试多界面交互。它们运行的比本地测试慢很多,所以谷歌建议最好是必须针对设备测试的时候才使用。
1. 搭建测试环境
在 Android 项目中,插桩单元测试的源文件目录在 module-name/src/androidTest/java/ 中。当项目创建完成时则存在。在开始之前,我们需要添加 AndroidX Test 相关的依赖。
dependencies {
androidTestImplementation 'androidx.test:runner:1.1.0'
androidTestImplementation 'androidx.test:rules:1.1.0'
// Optional -- Hamcrest library
androidTestImplementation 'org.hamcrest:hamcrest-library:1.3'
// Optional -- UI testing with Espresso
androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0'
// Optional -- UI testing with UI Automator
androidTestImplementation 'androidx.test.uiautomator:uiautomator:2.2.0'
}
如需使用 JUnit 4 测试类,请务必在您的项目中将 AndroidJUnitRunner 指定为默认插桩测试运行程序,方法是在应用的模块级 build.gradle 文件中添加以下设置:
android {
defaultConfig {
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
}
2. 创建插桩单元测试类
插桩单元测试类是一个 JUnit4 测试类,需要指定执行器。
import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4ClassRunner.class)
public class InstrumentationTest {
@Test
public void createLog() {
}
}
2.3. Android 编译配置
首先需要在项目的根目录中创建一个 .gitlab-ci.yml 文件,然后填写对应的执行脚本。
image: runmymind/docker-android-sdk
before_script:
- chmod +x ./gradlew
stages:
- build
assembleDebug:
stage: build
script:
- ./gradlew clean
- ./gradlew assembleDebug
artifacts:
paths:
- app/build/outputs/
only:
- master
assembleRelease:
stage: build
script:
- ./gradlew clean
- ./gradlew assembleRelease
artifacts:
paths:
- app/build/outputs/
only:
- master
不使用镜像,自己下载安装。
image: openjdk:8-jdk
# 定义变量
variables:
ANDROID_COMPILE_SDK: "28"
ANDROID_BUILD_TOOLS: "28.0.2"
ANDROID_SDK_TOOLS: "4333796"
# 安装包,安装 Android SDK,配置环境
before_script:
- apt-get --quiet update --yes
- apt-get --quiet install --yes wget tar unzip lib32stdc++6 lib32z1
- wget --quiet --output-document=android-sdk.zip https://dl.google.com/android/repository/sdk-tools-linux-${ANDROID_SDK_TOOLS}.zip
- unzip -d android-sdk-linux android-sdk.zip
- echo y | android-sdk-linux/tools/bin/sdkmanager "platforms;android-${ANDROID_COMPILE_SDK}" >/dev/null
- echo y | android-sdk-linux/tools/bin/sdkmanager "platform-tools" >/dev/null
- echo y | android-sdk-linux/tools/bin/sdkmanager "build-tools;${ANDROID_BUILD_TOOLS}" >/dev/null
- export ANDROID_HOME=$PWD/android-sdk-linux
- export PATH=$PATH:$PWD/android-sdk-linux/platform-tools/
- chmod +x ./gradlew
# temporarily disable checking for EPIPE error and use yes to accept all licenses
- set +o pipefail
- yes | android-sdk-linux/tools/bin/sdkmanager --licenses
- set -o pipefail
# 定义状态
stages:
- build
- test
# 编译 app
lintDebug:
stage: build
script:
- ./gradlew -Pci --console=plain :app:lintDebug -PbuildDir=lint
assembleDebug:
stage: build
script:
- ./gradlew assembleDebug
artifacts:
paths:
- app/build/outputs/
# 执行测试
debugTests:
stage: test
script:
- ./gradlew -Pci --console=plain :app:testDebug
2.4. Android 编译检查
2.4.1. lint 工具检查
Lint 工具是 Android Studio 自带的检查工具,可以方便检查一些针对 Android 的问题。更详细的使用可以参照:gudong.site/2021/01/17/…
./gradlew lint |
|---|
2.4.2. 代码覆盖率
./gradlew clean createDebugCoverageReport |
|---|
2.4.3. 模拟器
模拟器的启动可以通过 emulator 指令。
emulator -avd avd_name [ {-option [value]} … ] |
|---|
对于从 Android Studio 启动时,一般的指令格式如下:
/Users/antway/Library/Android/sdk/emulator/emulator -avd Nexus_5X_API_23 -netdelay none -netspeed full |
|---|
如需查看 AVD 名称的列表,请输入以下命令:
emulator -list-avds |
|---|
与此同时,我们可以在对应的 SDK 目录文件夹下面查看对应的模拟器 Android 系统镜像。
Mac OS X 和 Linux - ~/Library/Android/sdk/system-images/android-apiLevel/variant/arch/ |
|---|
查看 AVD 信息。AVD 数据目录(也称为内容目录)特定于单个 AVD 实例,包含 AVD 的所有可修改数据。
Mac OS X 和 Linux - ~/.android/avd/name.avd/ |
|---|
2.4.4 其它检测指令
check - 运行所有检查.
connectedAndroidTest - 在已连接的设备上安装所有flavors(渠道包)并运行instrumentation测试
connectedCheck - 在当前已连接的设备上运行设备检测.
connectedDebugAndroidTest - 在已连接的设备上安装并运行Debug版本的测试.
deviceAndroidTest - 在所有提供的设备上安装并运行instrumentation测试.
deviceCheck - 在所有提供的设备和测试服务器上运行设备检测.
lint - 在所有变种版本上运行lint检测.
lintDebug - 在Debug版本上运行lint检测.
lintRelease - 在Release版本上运行lint检测.
test - 在所有变种版本上运行单元测试.
testDebugUnitTest - 在Debug版本上运行单元测试.
testReleaseUnitTest - 在Release版本上运行单元测试