Medindo a qualidade do código do aplicativo Android com Sonarqube e Jacoco em 2019


Olá Habr!


Meu nome é Artyom Dobrovinsky, trabalho como desenvolvedor Android na FINCH .


Uma vez, depois de alguns momentos com um colega de uma empresa que está colocando anúncios para vender MIGs e mosquitos chamados Igor, começamos a discutir analisadores de código estático no CI (e o que mais discutir). Expressou-se a idéia de que eles eram legais de usar - mas somente depois que havia confiança na confiabilidade lógica do código. Em outras palavras, você pode pensar no estilo do código somente após a gravação de todos os testes.


Decidi ouvir o meu colega e pensei em como calcular a escala do desastre para aplicativos improvisados. A vista caiu sobre Sonarqube e Jacoco. O processo de conectá-los para projetos hello-world é elementar. Conectá-los em um projeto Android, dividido em módulos, já é mais difícil. Para ajudar os interessados, este artigo foi escrito.


Habré já tem uma tradução muito boa do tutorial sobre o uso do Sonarqube - mas é a partir de 2016, algo está desatualizado lá, não há kotlin e só acho a geração de relatórios para todos os buildType redundantes.


Um pouco sobre bibliotecas, para aqueles que não estão familiarizados com elas.


O Sonarqube é uma plataforma de código aberto para inspeção e medição contínuas da qualidade do código. Ele permite que você acompanhe a luta contra a dívida técnica ao longo do tempo (é legal ver que a dívida técnica está vencendo e você não pode fazer nada a respeito). O Sonar também monitora código duplicado, possíveis vulnerabilidades e complexidade excessiva de funções.


Jacoco é uma biblioteca gratuita para calcular a cobertura de teste de um projeto em Java. Mas com Kotlin vamos fazer amigos dela.


Como conectar o Sonarqube e o Jacoco


No build.gradle do módulo raiz, adicione o seguinte 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 - indique onde o Sonar deve colocar o relatório para análise posterior.
sonar.projectBaseDir especifique a pasta na qual a análise será iniciada inicialmente; no nosso caso, este é $ rootDir - a pasta raiz do projeto.
lista de exceções do sonar.coverage.exclusions para contar a cobertura, onde ** é qualquer pasta, * é qualquer nome ou resolução de arquivo.
sonar.sources é a pasta de origem.
sonar.tests - uma linha vazia aqui para que os testes também possam ser analisados ​​pelo Sonarqube.
sonar.coverage.exclusions - excluímos testes da análise de cobertura de testes.
sonar.coverage.jacoco.xmlReportPaths - usando collect() coletamos relatórios do Jacoco para calcular a cobertura do teste.


Para ativar o Jacoco, é melhor criar um arquivo jacoco.gradle e escrever toda a lógica necessária lá. Isso ajudará a evitar bagunçar o outro build.gradle.


Para não registrar o Jacoco no build.gradle de cada subprojeto, prescrevemos sua inicialização no fechamento dos subprojetos. No reportsDirPath para submódulos, especifique a pasta raiz. A partir daí, o Sonar receberá todos os relatórios do Jacoco.


 subprojects { apply plugin: 'jacoco' jacoco { toolVersion = '0.8.5' def reportsDirPath = "${project.rootDir}/app/build/reports/jacoco/${project.name}" reportsDir = file(reportsDirPath) } } 

No mesmo arquivo, escrevemos uma função para configurar o Jacoco.
Essa função é ótima, então primeiro eu a trarei - e depois explicarei o que está acontecendo nela.


 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) } } 

Criamos a tarefa getJacocoReports , o grupo "Reporting". Os relatórios serão fornecidos nos formatos html e xml. Todos os arquivos, exceto aqueles incluídos na matriz de exclusões, serão analisados. Além dos arquivos Androyd gerados, decidi excluir da análise todos os fragmentos e atividades, interfaces de Retrofit, pacote com DI, visualizações personalizadas e código da biblioteca.
Talvez essa lista mude com o tempo.


classDirectories - uma indicação de onde procurar código para análise. Nós incluímos os arquivos java e kotlin aqui.
sourceDirectories - especifique onde o Jacoco procurará os arquivos de origem.
executionData - como no caso do Sonar, uma indicação de onde o relatório será gerado para calcular a cobertura.


Também no jacoco.gradle você precisa adicionar sua configuração para todos os módulos usando a função acima:


 allprojects { project -> configureJacoco(project) project.tasks.withType(Test) { enabled = true jacoco.includeNoLocationClasses = true } } 

E uma tarefa para coletar os relatórios gerados:


 task getJacocoReports() { group = "Reporting" subprojects.forEach { subproject -> subproject.tasks.withType(JacocoReport).forEach { task -> dependsOn task } } } 

Executando o Sonarqube pela linha de comando


./gradlew % % && ./gradlew jacocoAggregateReports && ./gradlew sonarqube simplesmente: ./gradlew % % && ./gradlew jacocoAggregateReports && ./gradlew sonarqube . Os comandos são executados em && , porque a execução deve ser interrompida se a etapa anterior não tiver êxito.


O que acontece no comando acima:


  1. Primeiro, execute os testes (ao mesmo tempo em que geramos todos os arquivos necessários na pasta de compilação).
  2. Gere um relatório Jacoco.
  3. Inicie o Sonarqube.

Em seguida, você precisa ir ao site, falhar no projeto e analisar a escala do desastre. A página do projeto mostra o resultado da última verificação.


Com Sonarqube, a ideia do estado do projeto está se tornando muito mais completa. É mais fácil ajustar o atraso da dívida técnica do que enfrentar desenvolvedores iniciantes (em cada esquiva, o Sonarqube argumenta por que não é aceito escrever assim - ler essas explicações pode ser muito útil) e, simplesmente - o conhecimento é poder .


Isso é tudo, pessoal!


Pergunta aos leitores - o que você usa para analisar o código e medir a cobertura do teste? Você vê o ponto?

Source: https://habr.com/ru/post/pt475784/


All Articles