Android项目集成Jenkins(JUnit test & Coverage)

为了实现持续集成,提高代码质量,项目要求集成Jenkins,第一次集成Jenkins,花了不少时间,终于还是完成了,这里记录一下整个过程,Jenkins支持很多功能,Android Lint、Check Style、PMD、FindBugs、JUnit Test Report、Coverage Report等等,做下来感觉Coverage这部分的集成相对困难,所以本文主要以”如何在Jenkins中集成Junit & Coverage Report“为主题(至于怎么写JUnit test,不是本文的主题,我们假设项目已经支持JUnit环境了)。

关于Jenkins的集成,网上的文章不算多,这里有一个系列博客比较有参考价值:
[url]http://blog.youkuaiyun.com/it_talk/article/details/50267573[/url]
有兴趣的可以深入研究一下。我这里会简略提一些必要的集成步骤,还会补充一些集成过程中遇到问题的解决。

1.编写coverage.gradle:
我先把最终的完成脚本贴出来,然后再分别说明。

//代码覆盖率插件
apply plugin: 'jacoco'

android {
// 目前已经不支持通过jacoco{}指定版本了,如果加入这块代码,运行不会出错,但是会有
// warning提示
// jacoco{
// version "0.7.6.201602180812"
// }

// 项目较小的情况下不需要dexOptions的配置,只有项目方法数超过64K的限制时或者
// 项目本身接近64K上限但还未超过,加了Junit代码后就超过时(我们项目就属于这种
// 情况),需要对项目做multidex支持,实现方法很简单,请自行调查。
// Enable multidex后可能会遇到java.lang.OutOfMemoryError: GC overhead limit exceeded问题,需要此项配置来解决
dexOptions {
javaMaxHeapSize "4g"
}

buildTypes {
debug{
testCoverageEnabled true
}
}
}

//jacocoTestReport依赖于connectedAndroidTest task,所以在执行jacoco之前需要先执行connectedAndroidTest,也就是说需要连接测试机(模拟器or真机)
task jacocoAndroidTestReport(type:JacocoReport,dependsOn:"connectedAndroidTest"){
group = "Reporting"
description = "Generate Jacoco coverage reports after running tests."
reports{
xml.enabled false
html.enabled true
csv.enabled false
}
classDirectories = fileTree(
dir : "$buildDir/intermediates/classes/debug",
excludes : [
'**/*Test.class',
'**/R.class',
'**/R$*.class',
'**/BuildConfig.*',
'**/Manifest*.*'
]
)
def coverageSourceDirs = ['src']
additionalSourceDirs = files(coverageSourceDirs)
sourceDirectories = files(coverageSourceDirs)
additionalClassDirs = files(coverageSourceDirs)
executionData = files("$buildDir/outputs/androidTest-code-coverage/connected/coverage.ec")
}

//jacocoTestReport依赖于test task,所以在执行jacoco之前需要先执行test
task jacocoTestReport(type:JacocoReport,dependsOn:"test"){
group = "Reporting"
description = "Generate Jacoco coverage reports after running tests."
reports{
xml.enabled false
html.enabled true
csv.enabled false
}
classDirectories = fileTree(
dir : "$buildDir/intermediates/classes/debug",
excludes : [
'**/*Test.class',
'**/R.class',
'**/R$*.class',
'**/BuildConfig.*',
'**/Manifest*.*'
]
)
def coverageSourceDirs = ['src']
additionalSourceDirs = files(coverageSourceDirs)
sourceDirectories = files(coverageSourceDirs)
additionalClassDirs = files(coverageSourceDirs)
executionData = files("$buildDir/outputs/test-code-coverage/coverage.ec")
}


其中有两个task:jacocoAndroidTestReport、jacocoTestReport。从他们各自的依赖关系

task jacocoAndroidTestReport(type:JacocoReport,dependsOn:"connectedAndroidTest")

task jacocoTestReport(type:JacocoReport,dependsOn:"test")

我们可以知道jacocoAndroidTestReport其实是针对androidTest测试的,而jacocoTestReport是针对test测试的。androidTest和test是什么?简单的说androidTest就是跑在Android模拟器(AVD)或者真机上的测试代码(包括Android UI测试),test就是跑在jvm上的测试代码,其实就是纯java的iunit test(和server端的junit没啥分别),他们还有各自对应的compile——”androidTestCompile"和"testCompile",关于这部分知识,不清楚的同学可以自行google,总之根据项目需要选择task。

2.将coverage.gradle导入项目的build.gradle中:

apply plugin: 'com.android.library'
// 这里是相对路径,假设coverage.gradle和build.gradle在同一个目录下
apply from: './coverage.gradle'

这样本地的脚本就算完成了,接下来需要配置Jenkins。Jenkins集成coverage其实会依赖JAVA、Android、Gradle环境,如果Jenkins搭在本地,可能就不需要这些步骤(毕竟做开发的不可能没有这些环境),但如果是搭在一台新的远程服务器上,那你可能需要事先搭建以上3个环境,配好环境变量,我没有实际验证过这3个环境是不是必须的,只是我们的远程服务器上Jenkins运行gradle脚本时一开始提示“can't find project ‘gradle’”,于是我单独配置了这些环境,之后还需要保证Jenkins服务器的网络Fan Qiang,否则很可能gradle脚本运行过程中下载一些必要的依赖会失败。

首先在Jenkins上新建一个“构建”,指定要运行的gradle task:
[img]http://dl2.iteye.com/upload/attachment/0119/6056/88f02603-02ae-3713-a9ec-5e76a3003dbe.png[/img]
起初我是选择的“Invoke Gradle”,这个需要在服务器上配置gradle环境,但我配好运行还是有问题,所以我换了“Use Gradle Wrapper”,Jenkins会使用我们的gradle wrapper去自动下载gradle,这里就需要保证网络畅通了。“Root Build Script”就是指定你的build.gradle路径,如果不确定${workspace}指向的路径到底是哪里,可以直接运行一下,根据Jenkins日志提示的错误判断出来,总之你要指定正确的build.gradle路径,使Jenkins能找到它,之所以添加一个clean的task,是因为junit运行一次后下次运行如果不删除之前的结果会报错,所以运行之前先clean一次。

然后添加一个”构建后操作“,选择“Publish Junit test result report”:
[img]http://dl2.iteye.com/upload/attachment/0119/6064/17a02aaf-6f80-32ba-a5bc-834b1b2fb481.png[/img]
xml的路径设定为JUnit执行后的XML报告的路径。

之后再添加一个”构建后操作“,选择“Record JaCoCo coverage report”:
[img]http://dl2.iteye.com/upload/attachment/0119/6086/193ef2c0-e515-3322-ad96-ff9a771c2167.png[/img]
“Path to class directories”配置的是编译后.class文件的路径地址,Android都是放在build路径下build\intermediates\classes;“Path to source directories”配置的是java代码路径。

这样一来,Jenkins就会在构建过程中执行我们的task,生成junit结果报告和coverage报告,构建后就会自动收集结果病生成报告。

[size=large]接下来我们分析一下coverage.gradle,以及上述集成过程中可能会遇到的问题[/size]

一般来说,通过以下4步即可完成覆盖率的集成:
(1)引入jacoco插件;

//代码覆盖率插件
apply plugin: 'jacoco'

(2)打开coverage:

android {
buildTypes {
debug{
testCoverageEnabled true
}
}
}

(3)创建task:
即jacocoAndroidTestReport 和 jacocoTestReport。
(4)将coverage.gradle导入项目的build.gradle中:

apply plugin: 'com.android.library'
// 这里是相对路径,假设coverage.gradle和build.gradle在同一个目录下
apply from: './coverage.gradle'

如此,你的项目应该已经集成了coverage功能,但是如果你的项目很大,不得不分包(只有androidTest下面的测试可能会遇到该问题)时,就需要对项目做multidex的支持,之后你会发现64K的问题没有了,但是你可能又遇到新的问题:

Error:UNEXPECTED TOP-LEVEL ERROR:
java.lang.OutOfMemoryError: GC overhead limit exceeded

此时,下面的设置就派上用场了:

dexOptions {
javaMaxHeapSize "4g"
}

好了,GC的问题也解决了,终于能跑通了,但是控制台还是会出现一些warnings,虽然不影响构建过程,但强迫症的同学都不愿意看到它们,其实解决方法很简单。

warning_1:

To honour the JVM settings for this build a new JVM will be forked. Please consider using the daemon

解决方法:在gradle.propertie文件中增加以下配置

org.gradle.daemon=true


warning2:

Running dex as a separate process.
To run dex in process, the Gradle daemon needs a larger heap.
It currently has 1996 MB.
For faster builds, increase the maximum heap size for the Gradle daemon to at least 4608 MB (based on the dexOptions.javaMaxHeapSize = 4g).
To do this set org.gradle.jvmargs=-Xmx4608M in the project gradle.properties.
For more information see https://docs.gradle.org/current/userguide/build_environment.html

关于这个warning的原因,可以看这里[url]http://stackoverflow.com/questions/37090135/to-run-dex-in-process-the-gradle-daemon-needs-a-larger-heap-it-currently-has-a[/url],其实就是你指定了 javaMaxHeapSize "4g" 后引起的(android studio默认的是1g)
解决方法:在gradle.propertie文件中增加以下配置

# Default value: -Xmx1024m -XX:MaxPermSize=256m
org.gradle.jvmargs=-Xmx4608m -XX:MaxPermSize=1024m

以上就是整个过程了,如果大家在集成中遇到其他问题,就请自行解决吧。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值