Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationEventAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
9
Help us understand the problem. What is going on with this article?

More than 1 year has passed since last update.

Organization

Gradle6系 + Jacoco + マルチモジュール + フルKotlin + Android + Robolectric環境でユニットテストのカバレッジを出す

何番煎じなのか分かりませんが、Gradleが6系になりまた記述方法が変わったりしたので気にせずに記事にします。

  • Gradle: 6.0.1
  • Android Gradle Plugin: 3.5.3

の環境で検証しています。
また、カバレッジの算出に不要な記述は省いています。

実際のプロジェクトでどのように書くか気になる場合は以下を参照
https://github.com/ohmae/touch-icon-extractor

シングルモジュール

まずはマルチモジュールの前に各モジュールの中でのカバレッジ算出です。

apply plugin: "jacoco"

android {
    testOptions {
        unitTests.includeAndroidResources = true
        unitTests.all {
            jacoco {
                includeNoLocationClasses = true
            }
        }
    }
}

jacoco {
    toolVersion = "0.8.5"
}

task jacocoTestReport(
    type: JacocoReport,
    dependsOn: "testDebugUnitTest",
    group: "verification"
) {
    reports {
        xml.enabled = true
        html.enabled = true
    }
    getSourceDirectories().from = "${projectDir}/src/main/java"
    getClassDirectories().from = fileTree(dir: "${buildDir}/tmp/kotlin-classes/debug")
    getExecutionData().from = "${buildDir}/jacoco/testDebugUnitTest.exec"
}

これだけです。ビルドバリアントに依存する部分がありますので、その部分は適宜書き換えてください。

android {
    testOptions {
        unitTests.all {
            jacoco {
                includeNoLocationClasses = true
            }
        }
    }
}

上記の記述がないと、Robolectricを使ったテストのカバレッジが算出できません。

getClassDirectories().from = fileTree(dir: "${buildDir}/tmp/kotlin-classes/debug")

上記の部分はコンパイルされたクラスファイルの指定です、Androidでは自動的に生成されるクラスを除外する記述を行いますが、(現時点では)javaとkotlinでクラスファイルの場所が違っており、フルKotlinのプロジェクトならKotlinのクラスファイルを指定するだけで除外する必要はありません。

マルチモジュール

マルチモジュールでプロジェクト全体のカバレッジを出すには、各プロジェクトごとのカバレッジ情報を集める必要があります。
これはルートプロジェクトのGradleファイル上でタスクを定義します。

apply plugin: "jacoco"

jacoco {
    toolVersion = "0.8.5"
}

task jacocoMerge(
    type: JacocoMerge,
    group: "verification"
) {
    gradle.afterProject { project, _ ->
        if (project.rootProject != project && project.plugins.hasPlugin('jacoco')) {
            executionData "${project.buildDir}/jacoco/testDebugUnitTest.exec"
        }
    }
}

task jacocoMergedReport(
    type: JacocoReport,
    dependsOn: [tasks.jacocoMerge],
    group: "verification"
) {
    getExecutionData().from = jacocoMerge.destinationFile

    gradle.afterProject { project, _ ->
        if (project.rootProject != project && project.plugins.hasPlugin('jacoco')) {
            getSourceDirectories().from += "${project.projectDir}/src/main/java"
            getClassDirectories().from += project.fileTree(dir: "${project.buildDir}/tmp/kotlin-classes/debug")
        }
    }
    reports {
        xml.enabled = true
        html.enabled = true
    }
}

ルートプロジェクトはサブプロジェクトよりも先に評価されるため。そのままではサブプロジェクトの情報を集められない。
gradle.afterProject を使って、各プロジェクトが評価された後にクロージャを経由して情報を収集する。
クロージャではルートプロジェクト自身と、Jacocoを使っていない、カバレッジ算出対象外のプロジェクトを除外しています。
モジュールごとの算出と同じものを集めているだけといった形です。

jacocoMergeの依存関係で各モジュールのテスト実行をさせたかったのですがうまくいかず、補足いただけると幸いです。

以上です。

9
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
9
Help us understand the problem. What is going on with this article?