关于Jenkins的集成,网上的文章不算多,这里有一个系列博客比较有参考价值:
http://blog.csdn.net/it_talk/article/details/50267573
有兴趣的可以深入研究一下。我这里会简略提一些必要的集成步骤,还会补充一些集成过程中遇到问题的解决。
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:
起初我是选择的“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”:
xml的路径设定为JUnit执行后的XML报告的路径。
之后再添加一个”构建后操作“,选择“Record JaCoCo coverage report”:
“Path to class directories”配置的是编译后.class文件的路径地址,Android都是放在build路径下build\intermediates\classes;“Path to source directories”配置的是java代码路径。
这样一来,Jenkins就会在构建过程中执行我们的task,生成junit结果报告和coverage报告,构建后就会自动收集结果病生成报告。
接下来我们分析一下coverage.gradle,以及上述集成过程中可能会遇到的问题
一般来说,通过以下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的原因,可以看这里 http://stackoverflow.com/questions/37090135/to-run-dex-in-process-the-gradle-daemon-needs-a-larger-heap-it-currently-has-a,其实就是你指定了 javaMaxHeapSize "4g" 后引起的(android studio默认的是1g)
解决方法:在gradle.propertie文件中增加以下配置
# Default value: -Xmx1024m -XX:MaxPermSize=256m org.gradle.jvmargs=-Xmx4608m -XX:MaxPermSize=1024m
以上就是整个过程了,如果大家在集成中遇到其他问题,就请自行解决吧。