
哈Ha!
我叫Artyom Dobrovinsky,我是FINCH的一名Android开发人员。
一次,在与一家公司的同事进行了几品脱后,该公司正在投放广告来销售MIG和蚊子,名为Igor,然后,我们开始讨论CI中的静态代码分析器(以及其他讨论内容)。 有人提出使用它们很酷的想法-但只有在对代码的逻辑可靠性有了信心之后。 换句话说,只有在编写完所有测试后,您才能考虑代码样式。
我决定听取我的同事的意见,并思考如何为简易应用程序计算灾难的规模。 观点落在Sonarqube和Jacoco身上。 将它们连接到hello-world项目的过程很基本。 将它们连接到细分为模块的Android项目中已经变得更加困难。 为了帮助那些有兴趣的人,写了这篇文章。
Habré已经对使用Sonarqube 的教程进行了很好的翻译 -但是从2016年开始,那里已经过时了,没有kotlin,只是我发现所有buildType冗余的报告生成。
关于不熟悉图书馆的人的一些知识。
Sonarqube是一个开源平台,用于持续检查和衡量代码质量。 它使您能够跟踪一段时间内与技术债务的斗争(很高兴看到技术债务正在获胜,您对此无能为力)。 Sonar还监视重复的代码,潜在的漏洞以及功能的过度复杂性。
Jacoco是一个免费的库,用于计算Java项目的测试覆盖率。 但是有了Kotlin,我们将结识她的朋友。
如何连接Sonarqube和Jacoco
在根模块的build.gradle中,添加以下代码:
apply plugin: 'android.application' apply plugin: 'org.sonarqube' sonarqube { properties { property "sonar.host.url", "%url sonarqube%" property "sonar.login", "%%" property "sonar.projectName", "% %" property "sonar.projectKey", "% %" property "sonar.reportPath", "${project.buildDir}/sonarqube/test.exec" property "sonar.projectBaseDir", "$rootDir" property "sonar.sources", "." property "sonar.tests", "" property "sonar.coverage.exclusions", "**/src/androidTest/**, **/src/test/**" property "sonar.coverage.jacoco.xmlReportPaths", fileTree(include: ['*/*/jacoco*.xml'], dir: "$rootDir/app/build/reports/jacoco").collect() } }
sonar.reportPath
指示声纳应将报告放在何处以进行进一步分析。
sonar.projectBaseDir
指定最初将在其中开始分析的文件夹; 在我们的例子中,这是$ rootDir-项目的根文件夹。
sonar.coverage.exclusions
列出了用于计算覆盖率的例外,其中**是任何文件夹,*是任何文件名或分辨率。
sonar.sources
是源文件夹。
sonar.tests
在此处为空行,因此Sonarqube也可以分析测试。
sonar.coverage.exclusions
我们从测试覆盖率分析中排除测试。
sonar.coverage.jacoco.xmlReportPaths
使用collect()
收集Jacoco报告以计算测试覆盖率。
要激活Jacoco,最好创建一个jacoco.gradle
文件并在其中写入所有必要的逻辑。 这将有助于避免使另一个build.gradle混乱。
为了不在每个子项目的build.gradle中注册Jacoco,我们在子项目闭包中规定了其初始化。 在reportsDirPath
for submodules中,指定根文件夹。 Sonar将在此获取Jacoco的所有报告。
subprojects { apply plugin: 'jacoco' jacoco { toolVersion = '0.8.5' def reportsDirPath = "${project.rootDir}/app/build/reports/jacoco/${project.name}" reportsDir = file(reportsDirPath) } }
在同一文件中,我们编写了一个配置Jacoco的函数。
这个功能很棒,所以我先带它-然后再解释其中发生了什么。
def configureJacoco = { project -> def variantName = project.name project.tasks.create(name: "getJacocoReports", type: JacocoReport) { group = "Reporting" description = "Generate Jacoco coverage reports for the $variantName build." reports { html.enabled = true xml.enabled = true } def excludes = [ '**/R.class', '**/R$*.class', '**/BuildConfig.*', '**/Manifest*.*', '**/AndroidManifest.xml', '**/*Test*.*', 'android/**/*.*', 'androidx/**/*.*', '**/*Fragment.*', '**/*Activity.*', '**/*Api.*', '**/injection/**/*.class', '**/ui/**/*.class', % build- % ] def javaClasses = fileTree(dir: "${project.buildDir}/intermediates/javac", excludes: excludes) def kotlinClasses = fileTree(dir: "${project.buildDir}/tmp/kotlin-classes", excludes: excludes) classDirectories = files([javaClasses, kotlinClasses]) sourceDirectories = files([ "${project.projectDir}/src/main/java", "${project.projectDir}/src/main/kotlin", ]) executionData = files(fileTree(include: ['*.exec'], dir: "${project.buildDir}/jacoco").files) } }
我们创建了getJacocoReports
任务,即“报告”组。 报告将以html和xml格式提供。 将分析除excludes数组中包含的文件以外的所有文件。 除了生成的Androyd文件之外,我决定从分析中排除所有片段和活动,Retrofit接口,带有DI的程序包,自定义视图和库代码。
也许这个列表会随着时间而改变。
classDirectories
指示在何处查找要分析的代码。 我们在此处同时包含java和kotlin文件。
sourceDirectories
指定Jacoco在何处查找源文件。
executionData
与Sonar一样,指示将在何处生成报告以计算覆盖率。
同样在jacoco.gradle中,您需要使用上述功能为所有模块添加其配置:
allprojects { project -> configureJacoco(project) project.tasks.withType(Test) { enabled = true jacoco.includeNoLocationClasses = true } }
还有一个用于收集生成的报告的任务:
task getJacocoReports() { group = "Reporting" subprojects.forEach { subproject -> subproject.tasks.withType(JacocoReport).forEach { task -> dependsOn task } } }
通过命令行运行Sonarqube
它的./gradlew % % && ./gradlew jacocoAggregateReports && ./gradlew sonarqube
简单: ./gradlew % % && ./gradlew jacocoAggregateReports && ./gradlew sonarqube
。 命令通过&&
运行,因为如果上一步没有成功,则应该中断执行。
上面的命令会发生什么:
- 首先,运行测试(同时我们在build文件夹中生成所有必需的文件)。
- 生成Jacoco报告。
- 发射Sonarqube。
接下来,您需要转到站点,使项目失败,然后查看灾难的规模。 项目页面显示最后检查的结果。
使用Sonarqube,项目状态的想法变得更加完整。 调整技术债务积压要比接受新手开发人员容易(在每个小问题中,Sonarqube都提出了为什么不接受这样写的论点-阅读这些说明可能非常有用),并且简单地说, 知识就是力量 。
就是这样,伙计们!
给读者的问题-您使用什么来分析代码和衡量测试覆盖率? 您一点都明白吗?