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プラグインのみです。
どのバージョンが最新であるかを確認するには、 jCenterでorg.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 -> ... } }
最も重要な部分はループ内に配置するものですので、これを詳しく見てみましょう。
最初に、正しい大文字と小文字でタスク名を準備します。
-
sourceName
ビルドソース名、blueDebug
-
sourcePath
ビルドソースパスへのパス(sourcePath
blue/debug
-
testTaskName
コードカバレッジの測定タスクが依存するテストを実行するためのタスク。例:testBlueDebug
定義方法は次のとおりです。
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の記事でも似たようなコードを見たことがあるかもしれません。そのため、ほとんどのコードが説明なしで明確になっていることを願っています。
追加の注意に値する部品:
-
classDirectories
"excludes"
、レポートから除外するテンプレートをリストできます。 生成されたコード(クラスR
、依存性を注入するコードなど)または無視したいもの -
reports
-それぞれ公開または分析に必要かどうかに応じて、HTMLおよび/またはXMLレポートを許可します。
これがすべて
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/"
ます。
更新
- 2015-08-23:Gradleプラグイン1.3.0のjacoco.gradleスクリプトを修正しました。テストタスクの末尾に「UnitTest」が付いています。
- 2015-10-01:テキストのタスク名の接尾辞を修正しました。
- 2015-10-28:最新のAndroidプラグインのビルドパスの変更を修正しました。
ソースコード
JaCoCoの例 (GitHub)