
مرحبا يا هبر!
اسمي Artyom Dobrovinsky ، أعمل كمطور أندرويد في FINCH .
مرة واحدة ، بعد بضع مكاييل مع زميل من شركة تقدم إعلانات لبيع MIG والبعوض باسم Igor ، بدأنا في مناقشة تحليلات الشفرات الثابتة في CI (وماذا نناقش). تم التعبير عن الفكرة بأنها كانت سهلة الاستخدام - ولكن فقط بعد وجود ثقة في الموثوقية المنطقية للرمز. بمعنى آخر ، لا يمكنك التفكير في نمط الشفرة إلا بعد كتابة جميع الاختبارات.
قررت الاستماع إلى زميلي وفكرت في كيفية حساب حجم الكارثة للتطبيقات المرتجلة. وانخفض الرأي على Sonarqube وجاكوكو. عملية ربطها لمشاريع hello-world هي عملية ابتدائية. يعد توصيلهم بمشروع Android ، مقسمًا إلى وحدات ، أكثر صعوبة بالفعل. من أجل مساعدة المهتمين ، تمت كتابة هذه المقالة.
يوجد لدى Habré بالفعل ترجمة جيدة للغاية حول البرنامج التعليمي حول استخدام Sonarqube - لكن بدءًا من عام 2016 ، هناك شيء قديم هناك ، لا يوجد kotlin وأجد فقط جيلًا لتقديم التقارير لجميع برامج buildType الزائدة عن الحاجة.
قليلا عن المكتبات ، لأولئك الذين ليسوا على دراية بها.
Sonarqube هي عبارة عن منصة مفتوحة المصدر للفحص المستمر وقياس جودة الرمز. يتيح لك تتبع الحرب على الديون الفنية بمرور الوقت (من الجيد أن ترى أن الدين الفني ينتصر ، ولا يمكنك فعل أي شيء حيال ذلك). تقوم Sonar أيضًا بمراقبة الكود المكرر ومواطن الضعف المحتملة والتعقيد المفرط للوظائف.
Jacoco هي مكتبة مجانية لحساب تغطية اختبار لمشروع في جاوة. ولكن مع Kotlin سنجعل صديقاتها.
كيفية الاتصال Sonarqube وجاكوكو
في 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.reportPath
إلى المكان الذي يجب على Sonar وضع التقرير فيه لمزيد من التحليل.
حدد sonar.projectBaseDir
المجلد الذي سيتم فيه بدء التحليل في البداية ؛ في حالتنا ، هذا هو $ rootDir - المجلد الجذر للمشروع.
sonar.coverage.exclusions
سرد استثناءات لحساب التغطية ، حيث ** هو أي مجلد ، a * هو أي اسم الملف أو الدقة.
sonar.sources
هو المجلد المصدر.
sonar.tests
عبارة عن سطر فارغ هنا بحيث يمكن أيضًا تحليل الاختبارات بواسطة Sonarqube.
sonar.coverage.exclusions
- نستبعد الاختبارات من تحليل تغطية الاختبار.
sonar.coverage.jacoco.xmlReportPaths
- باستخدام collect()
بجمع تقارير Jacoco لحساب تغطية الاختبار.
لتنشيط Jacoco ، من الأفضل إنشاء ملف jacoco.gradle
وكتابة كل المنطق اللازم هناك. هذا سيساعد على تجنب تشويش build.gradle الآخر.
من أجل عدم تسجيل Jacoco في build.gradle من كل مشروع فرعي ، نحن نصف التهيئة في إغلاق المشاريع الفرعية. في reportsDirPath
الفرعية ، حدد المجلد الجذر. من هناك ، سوف تأخذ 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 التي تم إنشاؤها ، قررت أن أستبعد من التحليل جميع الشظايا والأنشطة ، واجهات التعديل التحديثي ، والحزمة مع DI ، وطرق العرض المخصصة ، ورمز المكتبة.
ربما ستتغير هذه القائمة بمرور الوقت.
classDirectories
- إشارة إلى مكان البحث عن رمز للتحليل. نحن ندرج كلا ملفات جافا و 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
. يتم تشغيل الأوامر من خلال &&
، لأنه يجب مقاطعة التنفيذ إذا لم تنجح الخطوة السابقة.
ما يحدث في الأمر أعلاه:
- أولاً ، قم بإجراء الاختبارات (في نفس الوقت نقوم بإنشاء جميع الملفات الضرورية في مجلد الإنشاء).
- إنشاء تقرير Jacoco.
- إطلاق Sonarqube.
بعد ذلك تحتاج إلى الذهاب إلى الموقع ، وفشل في المشروع وإلقاء نظرة على حجم الكارثة. تعرض صفحة المشروع نتيجة الفحص الأخير.
مع Sonarqube ، أصبحت فكرة حالة المشروع أكثر اكتمالا. من الأسهل ضبط تراكم الديون الفنية ، بدلاً من التعامل مع المطورين المبتدئين (في كل نقاش ، تقدم Sonarqube حجة لماذا من غير المقبول أن تكتب بهذه الطريقة - قراءة هذه التفسيرات يمكن أن تكون مفيدة للغاية) ، وببساطة - المعرفة هي القوة .
هذا كل شيء ، الناس!
سؤال للقراء - ماذا تستخدم لتحليل الشفرة وقياس تغطية الاختبار؟ هل ترى هذه النقطة على الإطلاق؟