
Halo, Habr!
Nama saya Artyom Dobrovinsky, saya bekerja sebagai pengembang Android di FINCH .
Suatu hari, setelah beberapa gelas bir dengan seorang kolega dari sebuah perusahaan yang memasang iklan untuk menjual MIG dan nyamuk bernama Igor, kami mulai membahas penganalisa kode statis di CI (dan apa lagi yang harus didiskusikan). Gagasan itu disuarakan bahwa mereka keren untuk digunakan - tetapi hanya setelah ada kepercayaan pada keandalan logis dari kode. Dengan kata lain, Anda dapat memikirkan gaya kode hanya setelah semua tes ditulis.
Saya memutuskan untuk mendengarkan rekan saya dan berpikir tentang bagaimana menghitung skala bencana untuk aplikasi improvisasi. Pandangan jatuh pada Sonarqube dan Jacoco. Proses menghubungkan mereka untuk proyek hello-world adalah dasar. Menghubungkan mereka ke proyek Android, dipecah menjadi modul, sudah lebih sulit. Untuk membantu mereka yang tertarik, artikel ini ditulis.
Habré sudah memiliki terjemahan yang sangat baik dari tutorial tentang cara menggunakan Sonarqube - tetapi dari 2016, ada sesuatu yang sudah ketinggalan zaman di sana, tidak ada kotlin dan hanya saya menemukan pelaporan generasi untuk semua redundansi buildType.
Sedikit tentang perpustakaan, bagi mereka yang tidak terbiasa dengannya.
Sonarqube adalah platform sumber terbuka untuk inspeksi dan pengukuran kualitas kode berkelanjutan. Ini memungkinkan Anda untuk melacak pertarungan melawan utang teknis dari waktu ke waktu (itu keren untuk melihat bahwa utang teknis menang, dan Anda tidak dapat melakukan apa-apa). Sonar juga memonitor kode duplikat, potensi kerentanan, dan kompleksitas fungsi yang berlebihan.
Jacoco adalah perpustakaan gratis untuk menghitung cakupan pengujian suatu proyek di Jawa. Tapi dengan Kotlin kita akan berteman dengannya.
Bagaimana menghubungkan Sonarqube dan Jacoco
Di build.gradle dari modul root, tambahkan kode berikut:
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
- menunjukkan di mana Sonar harus meletakkan laporan untuk analisis lebih lanjut.
sonar.projectBaseDir
menentukan folder tempat analisis akan dimulai pada awalnya; dalam kasus kami, ini adalah $ rootDir - folder root proyek.
sonar.coverage.exclusions
mencantumkan pengecualian untuk menghitung cakupan, di mana ** adalah folder apa pun, a * adalah nama file atau resolusi apa pun.
sonar.sources
adalah folder sumber.
sonar.tests
adalah baris kosong di sini sehingga tes juga dapat dianalisis oleh Sonarqube.
sonar.coverage.exclusions
- kami mengecualikan tes dari analisis cakupan tes.
sonar.coverage.jacoco.xmlReportPaths
- menggunakan collect()
mengumpulkan laporan Jacoco untuk menghitung cakupan pengujian.
Untuk mengaktifkan Jacoco, lebih baik membuat file jacoco.gradle
dan menulis semua logika yang diperlukan di sana. Ini akan membantu untuk menghindari kekacauan build.gradle lainnya.
Agar tidak mendaftarkan Jacoco di build.gradle masing-masing subproyek, kami meresepkan inisialisasi dalam penutupan subproyek. Dalam reportsDirPath
untuk submodules, tentukan folder root. Dari sana, Sonar akan mengambil semua laporan Jacoco.
subprojects { apply plugin: 'jacoco' jacoco { toolVersion = '0.8.5' def reportsDirPath = "${project.rootDir}/app/build/reports/jacoco/${project.name}" reportsDir = file(reportsDirPath) } }
Dalam file yang sama, kami menulis fungsi untuk mengkonfigurasi Jacoco.
Fungsi ini hebat, jadi pertama saya akan membawanya - dan kemudian saya akan menjelaskan apa yang terjadi di dalamnya.
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) } }
Kami menciptakan tugas getJacocoReports
, grup "Pelaporan". Laporan akan diberikan dalam format html dan xml. Semua file kecuali yang termasuk dalam array yang dikecualikan akan dianalisis. Selain file Androyd yang dihasilkan, saya memutuskan untuk mengecualikan dari analisis semua fragmen dan aktivitas, antarmuka Retrofit, paket dengan DI, tampilan khusus dan kode perpustakaan.
Mungkin daftar ini akan berubah seiring waktu.
classDirectories
- indikasi tempat mencari kode untuk analisis. Kami menyertakan file java dan kotlin di sini.
sourceDirectories
- tentukan di mana Jacoco mencari file sumber.
executionData
- seperti dalam kasus Sonar, indikasi di mana laporan akan dihasilkan untuk menghitung cakupan.
Juga di jacoco.gradle Anda perlu menambahkan konfigurasinya untuk semua modul menggunakan fungsi di atas:
allprojects { project -> configureJacoco(project) project.tasks.withType(Test) { enabled = true jacoco.includeNoLocationClasses = true } }
Dan tugas untuk mengumpulkan laporan yang dihasilkan:
task getJacocoReports() { group = "Reporting" subprojects.forEach { subproject -> subproject.tasks.withType(JacocoReport).forEach { task -> dependsOn task } } }
Menjalankan Sonarqube melalui baris perintah
Ini ./gradlew % % && ./gradlew jacocoAggregateReports && ./gradlew sonarqube
sederhana: ./gradlew % % && ./gradlew jacocoAggregateReports && ./gradlew sonarqube
. Perintah dijalankan melalui &&
, karena eksekusi harus terganggu jika langkah sebelumnya tidak berhasil.
Apa yang terjadi pada perintah di atas:
- Pertama, jalankan tes (pada saat yang sama kami menghasilkan semua file yang diperlukan dalam folder build).
- Buat laporan Jacoco.
- Luncurkan Sonarqube.
Selanjutnya Anda harus pergi ke situs, gagal dalam proyek dan melihat skala bencana. Halaman proyek menunjukkan hasil pemeriksaan terakhir.
Dengan Sonarqube, gagasan tentang keadaan proyek menjadi jauh lebih lengkap. Lebih mudah untuk menyesuaikan tumpukan utang teknis, daripada mengambil pengembang pemula (di setiap berdalih Sonarqube memberikan argumen mengapa tidak diterima untuk menulis seperti itu - membaca penjelasan ini bisa sangat berguna), dan hanya - pengetahuan adalah kekuatan .
Itu saja, semuanya!
Pertanyaan kepada pembaca - apa yang Anda gunakan untuk menganalisis kode dan mengukur cakupan tes? Apakah Anda melihat intinya sama sekali?