
Hola Habr!
Mi nombre es Artyom Dobrovinsky, trabajo como desarrollador de Android en FINCH .
Una vez, despu茅s de un par de pintas con un colega de una compa帽铆a que est谩 colocando anuncios para vender MIG y mosquitos llamados Igor, comenzamos a discutir analizadores de c贸digo est谩tico en CI (y qu茅 m谩s discutir). Se expres贸 la idea de que eran geniales de usar, pero solo despu茅s de que hubo confianza en la confiabilidad l贸gica del c贸digo. En otras palabras, puede pensar en el estilo de c贸digo solo despu茅s de escribir todas las pruebas.
Decid铆 escuchar a mi colega y pens茅 en c贸mo calcular la escala del desastre para aplicaciones improvisadas. La vista cay贸 sobre Sonarqube y Jacoco. El proceso de conectarlos para proyectos de hello-world es elemental. Conectarlos a un proyecto de Android, dividido en m贸dulos, ya es m谩s dif铆cil. Para ayudar a los interesados, este art铆culo fue escrito.
Habr茅 ya tiene una muy buena traducci贸n del tutorial sobre el uso de Sonarqube, pero es de 2016, algo est谩 desactualizado all铆, no hay kotlin y solo encuentro que la generaci贸n de informes para todos los buildType es redundante.
Un poco sobre bibliotecas, para aquellos que no est谩n familiarizados con ellas.
Sonarqube es una plataforma de c贸digo abierto para la inspecci贸n continua y la medici贸n de la calidad del c贸digo. Le permite seguir la lucha contra la deuda t茅cnica a lo largo del tiempo (es genial ver que la deuda t茅cnica est谩 ganando, y no puede hacer nada al respecto). Sonar tambi茅n supervisa el c贸digo duplicado, las vulnerabilidades potenciales y la excesiva complejidad de las funciones.
Jacoco es una biblioteca gratuita para calcular la cobertura de prueba de un proyecto en Java. Pero con Kotlin haremos sus amigos.
C贸mo conectar Sonarqube y Jacoco
En build.gradle del m贸dulo ra铆z, agregue el siguiente c贸digo:
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
: indica d贸nde debe colocar Sonar el informe para un an谩lisis m谩s detallado.
sonar.projectBaseDir
especifica la carpeta en la que se iniciar谩 el an谩lisis inicialmente; en nuestro caso, este es $ rootDir, la carpeta ra铆z del proyecto.
sonar.coverage.exclusions
enumerando excepciones para contar la cobertura, donde ** es cualquier carpeta, a * es cualquier nombre de archivo o resoluci贸n.
sonar.sources
es la carpeta de origen.
sonar.tests
es una l铆nea vac铆a aqu铆 para que Sonarqube tambi茅n pueda analizar las pruebas.
sonar.coverage.exclusions
: excluimos las pruebas del an谩lisis de cobertura de pruebas.
sonar.coverage.jacoco.xmlReportPaths
: mediante el m茅todo collect()
recopilamos informes de Jacoco para calcular la cobertura de la prueba.
Para activar Jacoco, es mejor crear un archivo jacoco.gradle
y escribir toda la l贸gica necesaria all铆. Esto ayudar谩 a evitar abarrotar el otro build.gradle.
Para no registrar a Jacoco en el build.gradle de cada subproyecto, prescribimos su inicializaci贸n en el cierre de los subproyectos. En reportsDirPath
para subm贸dulos, especifique la carpeta ra铆z. A partir de ah铆, Sonar tomar谩 todos los informes de Jacoco.
subprojects { apply plugin: 'jacoco' jacoco { toolVersion = '0.8.5' def reportsDirPath = "${project.rootDir}/app/build/reports/jacoco/${project.name}" reportsDir = file(reportsDirPath) } }
En el mismo archivo, escribimos una funci贸n para configurar Jacoco.
Esta funci贸n es excelente, as铆 que primero la traer茅 y luego explicar茅 lo que est谩 sucediendo en ella.
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) } }
Creamos la tarea getJacocoReports
, el grupo "Informes". Los informes se proporcionar谩n en formatos html y xml. Se analizar谩n todos los archivos, excepto los incluidos en la matriz exclude. Adem谩s de los archivos Androyd generados, decid铆 excluir del an谩lisis todos los fragmentos y actividades, las interfaces de actualizaci贸n, el paquete con DI, las vistas personalizadas y el c贸digo de la biblioteca.
Quiz谩s esta lista cambie con el tiempo.
classDirectories
: una indicaci贸n de d贸nde buscar el c贸digo para el an谩lisis. Incluimos archivos java y kotlin aqu铆.
sourceDirectories
: especifique d贸nde buscar谩 Jacoco los archivos fuente.
executionData
: como en el caso de Sonar, una indicaci贸n de d贸nde se generar谩 el informe para calcular la cobertura.
Tambi茅n en jacoco.gradle necesita agregar su configuraci贸n para todos los m贸dulos usando la funci贸n anterior:
allprojects { project -> configureJacoco(project) project.tasks.withType(Test) { enabled = true jacoco.includeNoLocationClasses = true } }
Y una tarea para recopilar los informes generados:
task getJacocoReports() { group = "Reporting" subprojects.forEach { subproject -> subproject.tasks.withType(JacocoReport).forEach { task -> dependsOn task } } }
Ejecutando Sonarqube a trav茅s de la l铆nea de comando
./gradlew % % && ./gradlew jacocoAggregateReports && ./gradlew sonarqube
simplemente: ./gradlew % % && ./gradlew jacocoAggregateReports && ./gradlew sonarqube
. Los comandos se ejecutan a trav茅s de &&
, porque la ejecuci贸n debe interrumpirse si el paso anterior no tuvo 茅xito.
Lo que sucede en el comando anterior:
- Primero, ejecute las pruebas (al mismo tiempo generamos todos los archivos necesarios en la carpeta de compilaci贸n).
- Generar un informe Jacoco.
- Lanzamiento Sonarqube.
Luego debe ir al sitio, fallar en el proyecto y observar la escala del desastre. La p谩gina del proyecto muestra el resultado de la 煤ltima verificaci贸n.
Con Sonarqube, la idea del estado del proyecto se est谩 volviendo mucho m谩s completa. Es m谩s f谩cil ajustar la acumulaci贸n de deuda t茅cnica que asumir desarrolladores novatos (en cada objeci贸n, Sonarqube da un argumento por el cual no se acepta escribir de esa manera; leer estas explicaciones puede ser muy 煤til), y simplemente: el conocimiento es poder .
隆Eso es todo, amigos!
Pregunta a los lectores: 驴qu茅 utilizan para analizar el c贸digo y medir la cobertura de la prueba? 驴Ves el punto en absoluto?