Jacoco & SonarQube
- JaCoCo是一个Java代码覆盖率库,它可以帮助开发人员了解他们的代码库中哪些代码已经被测试覆盖,哪些代码还需要进行测试。JaCoCo可以与各种构建工具(如Maven和Gradle)集成,以生成代码覆盖率报告。
- SonarQube是一个开源的代码质量管理平台,它可以帮助开发人员在整个开发周期中管理和提高代码质量。SonarQube可以与各种编程语言(如Java、C#、JavaScript等)集成,并提供了一系列功能,包括代码质量分析、代码覆盖率、代码复杂度、代码重复性、安全漏洞等方面的检测。
我们要利用这2个工具平台,使用构建工具Gradle给Android项目打造一个可视化代码覆盖率,首先,使用JaCoCo生成代码覆盖率报告,然后将报告传递给SonarQube进行分析。SonarQube将使用这些报告来提供更广泛的代码质量分析,包括代码复杂度、代码重复性、安全漏洞等方面的检测。
Jacoco集成到Gradle
- 添加plugin
classpath "org.jacoco:org.jacoco.core:0.8.10"
- 创建一个
jacoco.gradle
文件在需要单元测试覆盖的module里面 - 粘贴和修改如下代码,注意可以配置输出报告忽略文件和文件夹的覆盖率,通常是View层和一些工具类等
apply plugin: "jacoco" jacoco { toolVersion = '0.8.10' } // 配置需要忽略单元测试覆盖率的文件或文件夹 //* 匹配零个或多个字符 //** 匹配零个或多个目录 //? 匹配单个字符 project.ext.coverageExclusions = [ '**/BR.*', '**/R.*', '**/R$*.*', '**/Manifest*.*', "**/*Test*.*", '**/BuildConfig.*' ] def javaDebugTree = fileTree(dir: "${buildDir}/intermediates/javac/debug", excludes: coverageExclusions) def kotlinDebugTree = fileTree(dir: "${buildDir}/tmp/kotlin-classes/debug", excludes: coverageExclusions) task jacocoTestReport(type: JacocoReport, dependsOn: "testDebugUnitTest") { group = "Reporting" description = "Generate Jacoco coverage reports after running tests." reports { xml.getRequired().set(true) html.getRequired().set(true) } sourceDirectories.from = files(["${project.projectDir}/src/main/java"]) classDirectories.from = files([javaDebugTree, kotlinDebugTree]) // pattern '**/*' searches execution data in all sub-directories of build def execDataPatterns = ["**/*.exec", "**/*.ec"] executionData.from = files(fileTree(dir: "build", includes: execDataPatterns)) }
- 在需要的module的build.gradle中添加
apply from: "jacoco.gradle"
- 执行命令
./gradlew :module clean jacocoTestReport
- 单元测试全部成功后,检查文件夹
build/reports/jacoco/jacocoTestReport/
是否存在 - 右键
build/reports/jacoco/jacocoTestReport/html/index.html
Open in -> Browser - 根据包名展示当前代码覆盖的详细情况,点击包名可以查看java类的详情
SonarQube安装和部署
- 下载免费的社区版Community Edition: www.sonarqube.org/downloads/
- 检查是否安装和配置JDK的环境变量
- 解压并且打开目录
/bin
,控制台打开/bin/
目录选择操作系统对应文件夹进入 - 在控制台执行启动命令
./sonar.sh start
,支持命令{ console | start | stop | force-stop | restart | status | dump }
- 浏览器输入
127.0.0.1:9000
进入登录页面,默认账户密码admin/admin
- 生成 api Token, 右上角我的账户-> security -> Generate Tokens
SonarQube集成到Gradle
- 添加plugin
dependencies { classpath "org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:3.0" }
- 在根目录
build.gradle
配置,把Jacoco的report输出路径配置到jacoco.xmlReportPathsapply plugin: 'org.sonarqube' sonarqube { properties { property "sonar.projectName", "MVI-Hilt" property "sonar.projectKey", "MVI-Hilt" property "sonar.modules", ":app, :mvi" property "sonar.host.url", "http://127.0.0.1:9000" property "sonar.token", "squ_11f5a8e2654358fefc45420da045f50ab2dde76b" property "sonar.sources", "src/main/java, src/debug/java" property "sonar.tests", "src/test/java, src/androidTest/java" property "sonar.binaries", "build/intermediates/javac/debug/classes" property "sonar.androidLint.reportPaths", "build/reports/lint-results.xml" property "sonar.java.coveragePlugin", "jacoco" property "sonar.kotlin.coveragePlugin", "jacoco" property "sonar.junit.reportsPath", "build/reports/tests" property "sonar.coverage.jacoco.xmlReportPaths", "build/reports/coverage/debug/report.xml, build/reports/jacoco/jacocoTestReport/jacocoTestReport.xml" } }
- 在Android Studio中直接点击
sonarqube {...}
左边的绿色➡️,或者在控制台运行./gradlew sonarqube
- 浏览器打开网页
http://127.0.0.1:9000
- 点击项目可以查看扫描的报告详情,代码规范(code smell);代码漏洞;覆盖率;代码重复率等
- 项目配置github actions 或者jenkins ci/pipeline等,在提交PR之后PR中输出一份当前PR代码的报告,帮助Reivewer来检查当前代码是否有漏洞和代码规范问题。
Issue集锦
- 执行Task:sonarqube显示成功却没有任何反应,日志中输出Skipping SonarQube analysis: no properties configured, was it skipped in all projects?
解决方案:
gradle.properties
中删除org.gradle.unsafe.configuration-cache=true
- 执行Task: 报错 Can't add different class with same name: com/example/Xxx 解决方案:把报错的这个Class配置到 jacoco的Exclusions中,'com/example/**'
扩展阅读
代码质量管理,我们需要编写单元测试代码,之前的MVI系列文档中详细的介绍了Android中如何实现单元测试。另外在国内Android对单元测试等并没有很强的要求,很多开发者并不会写单元测试,这个时候官方的文档其实是最新和最权威,合适的学习途径也让我们事半功倍。 Android的MVI架构最佳实践(四):单元测试 - 掘金 (juejin.cn)