JaCoCoを使用したAndroidでのテストによるコードカバレッジの測定

投稿者:Mike Gouline
https://blog.gouline.net/2015/06/23/code-coverage-on-android-with-jacoco/
翻訳:セミヨン・ソルダテンコ

この機能は、Android Gradleプラグインバージョン0.10.0で登場して以来、テストによるコードカバレッジの測定(テストカバレッジ)について多くの記事が書かれています。 しかし、私を悩ませているのは、完全に機能するソリューションを得る前に、これらの記事のいくつかとGradleのドキュメントを見る必要があることです。 そこで、これを修正して時間を節約しようとする別の記事があります。

問題の声明


単体テストを含むAndroidプロジェクトがあり、実行されたテストのコードカバレッジレポートを作成します。 ソリューションは、さまざまなビルドモードと製品バリエーションをサポートする必要があります。

解決策


ソリューションはいくつかの部分で構成されているため、ステップごとに見ていきましょう。

コードカバレッジデータ収集を有効にする


テストを実行するビルドタイプのテストによるコードカバレッジのデータ収集のサポートを有効にする必要があります。 build.gradleには次のものが含まれている必要があります。

 android { ... buildTypes { debug { testCoverageEnabled = true } ... } ... } 

JaCoCoをセットアップする


このセクションはすべてbuild.gradleに配置できますが、このような「 build.gradleマウント」はビルドスクリプトを読み取れないようにするため、これらすべてを別のビルドスクリプトに入れてインポ​​ートすることをお勧めします。

プロジェクトのルートディレクトリにjacoco.gradleファイルを作成して、 jacoco.gradle構成を開始します。 任意の場所に作成できますが、プロジェクトのルートディレクトリに保存すると、すべてのサブプロジェクトから簡単に参照できます。

最も簡単な部分は、JaCoCoのインポートです。

 apply plugin: 'jacoco' jacoco { toolVersion = "0.7.5.201505241946" } 

jacocoプラグインを使用するために依存関係を宣言する必要はないことに注意してください-必要なのはAndroidプラグインのみです。

どのバージョンが最新であるかを確認するには、 jCenterorg.jacoco:org.jacoco.coreを探しますが、慎重に更新してください-最新バージョンはまだ互換性がない可能性があり、空のレポートなどの奇妙なことにつながる可能性があります。

次のステップは、すべての製品バリエーションとビルドモード用のGradleタスクを作成することです(実際、デバッグビルドのみをテストしますが、特別なデバッグビルド構成にこの機能を使用すると非常に便利です)。

 def buildTypes = android.buildTypes.collect { type -> type.name } def productFlavors = android.productFlavors.collect { flavor -> flavor.name } 

Groovyのcollectは入力としてリストを受け取り、リストの各要素で関数を呼び出し、結果を新しいリストで返すことに注意してください。 この場合、名前のリストに変換されるオブジェクトのリスト「アセンブリモード」と「製品バリエーション」が入力されます。

製品のバリエーションが指定されていないプロジェクトのために、空の名前を追加します。

 if (!productFlavors) productFlavors.add('') 

これで、基本的にGroovyのネストされたループである次のようにスクロールできます。

 productFlavors.each { productFlavorName -> buildTypes.each { buildTypeName -> ... } } 

最も重要な部分はループ内に配置するものですので、これを詳しく見てみましょう。

最初に、正しい大文字と小文字でタスク名を準備します。

定義方法は次のとおりです。

 def sourceName, sourcePath if (!productFlavorName) { sourceName = sourcePath = "${buildTypeName}" } else { sourceName = "${productFlavorName}${buildTypeName.capitalize()}" sourcePath = "${productFlavorName}/${buildTypeName}" } def testTaskName = "test${sourceName.capitalize()}UnitTest" 

これで、タスクは実際に次のようになります。

 task "${testTaskName}Coverage" (type:JacocoReport, dependsOn: "$testTaskName") { group = "Reporting" description = "Generate Jacoco coverage reports on the ${sourceName.capitalize()} build." classDirectories = fileTree( dir: "${project.buildDir}/intermediates/classes/${sourcePath}", excludes: ['**/R.class', '**/R$*.class', '**/*$ViewInjector*.*', '**/BuildConfig.*', '**/Manifest*.*'] ) def coverageSourceDirs = [ "src/main/java", "src/$productFlavorName/java", "src/$buildTypeName/java" ] additionalSourceDirs = files(coverageSourceDirs) sourceDirectories = files(coverageSourceDirs) executionData = files("${project.buildDir}/jacoco/${testTaskName}.exec") reports { xml.enabled = true html.enabled = true } } 

他のJaCoCoの記事でも似たようなコードを見たことがあるかもしれません。そのため、ほとんどのコードが説明なしで明確になっていることを願っています。

追加の注意に値する部品:

これがすべてjacoco.gradleについてであるため、ファイルの全内容は次のとおりです。

 apply plugin: 'jacoco' jacoco { toolVersion = "0.7.5.201505241946" } project.afterEvaluate { // Grab all build types and product flavors def buildTypes = android.buildTypes.collect { type -> type.name } def productFlavors = android.productFlavors.collect { flavor -> flavor.name } // When no product flavors defined, use empty if (!productFlavors) productFlavors.add('') productFlavors.each { productFlavorName -> buildTypes.each { buildTypeName -> def sourceName, sourcePath if (!productFlavorName) { sourceName = sourcePath = "${buildTypeName}" } else { sourceName = "${productFlavorName}${buildTypeName.capitalize()}" sourcePath = "${productFlavorName}/${buildTypeName}" } def testTaskName = "test${sourceName.capitalize()}UnitTest" // Create coverage task of form 'testFlavorTypeCoverage' depending on 'testFlavorTypeUnitTest' task "${testTaskName}Coverage" (type:JacocoReport, dependsOn: "$testTaskName") { group = "Reporting" description = "Generate Jacoco coverage reports on the ${sourceName.capitalize()} build." classDirectories = fileTree( dir: "${project.buildDir}/intermediates/classes/${sourcePath}", excludes: ['**/R.class', '**/R$*.class', '**/*$ViewInjector*.*', '**/*$ViewBinder*.*', '**/BuildConfig.*', '**/Manifest*.*'] ) def coverageSourceDirs = [ "src/main/java", "src/$productFlavorName/java", "src/$buildTypeName/java" ] additionalSourceDirs = files(coverageSourceDirs) sourceDirectories = files(coverageSourceDirs) executionData = files("${project.buildDir}/jacoco/${testTaskName}.exec") reports { xml.enabled = true html.enabled = true } } } } } 

結論として、このビルドスクリプトをappスクリプトに次のようにインポートする必要があります。

 apply from: '../jacoco.gradle' 

(注:これは、上記のように、 jacoco.gradleがプロジェクトのルートディレクトリにあることを意味します)

以上です! タスクが作成されたことを確認するには、 gradle tasksし、 "Reporting"セクションで次のようなものを探します。

 Reporting tasks --------------- testBlueDebugUnitTestCoverage - Generate Jacoco coverage reports on the BlueDebug build. testBlueReleaseUnitTestCoverage - Generate Jacoco coverage reports on the BlueRelease build. testRedDebugUnitTestCoverage - Generate Jacoco coverage reports on the RedDebug build. testRedReleaseUnitTestCoverage - Generate Jacoco coverage reports on the RedRelease build. 

レポートを作成するには、 gradle testBlueDebugUnitTestCoverageを実行します。 "build/reports/jacoco/testBlueDebugUnitTestCoverage/"ます。

更新



ソースコード


JaCoCoの例 (GitHub)

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


All Articles