Gradleを使用したマルチプロジェクトのテストレポート一括出力(Test Report Aggregation Plugin)

プラグインを使用したテストレポート一括出力のサンプルとして、下記の2パターンのマルチプロジェクトの例を記載する。
(書き方は基本的には一緒なので似たような説明が2回記載されているが、どっちでも使えることを示すための記載である。)

プロジェクト構成

パターン①

サブプロジェクトで共通の設定を「aa/build.gradle」に記載しているプロジェクト。

aa
 |
 |ーー aa-common
 |           |ーー build.gradle
 |
 |ーー aa-webapp
 |           |ーー build.gradle
 |
 |ーー aa-functions
 |           |ーー build.gradle
 |
 |ーー build.gradle

aa/build.gradle
※依存関係のバージョンは「gradle.properties」に定義

plugins {
  id 'java'
  id 'eclipse'
  id 'org.springframework.boot' version "${spring_boot_version}" apply false
  id 'io.spring.dependency-management' version "${spring_dependency_management_version}" apply false
}

jar {
  // ルートプロジェクトなのでjarを作成しない
  enabled = false
}

// サブプロジェクトで共通の設定・定義を記述する
subprojects {
  apply plugin: 'java'
  apply plugin: 'java-library'
  apply plugin: "eclipse"
  apply plugin: 'project-report'
  apply plugin: 'io.spring.dependency-management'

  // バージョン
  version = '1.0.0'

  // Javaのバージョン指定
  java.sourceCompatibility = JavaLanguageVersion.of(17)
  java.targetCompatibility = JavaLanguageVersion.of(17)

  // コンパイル時のエンコーディング指定
  [compileJava, compileTestJava]*.options*.encoding = "UTF-8"

  repositories {
    mavenCentral()
  }

  // コンパイル時にAnnotation Processor(lombok)を有効にする
  configurations {
    compileOnly {
      extendsFrom annotationProcessor
    }
    testCompileOnly {
      extendsFrom testAnnotationProcessor
    }
  }

  dependencies {
    // Spring
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'org.springframework.boot:spring-boot-starter-oauth2-client'
    implementation 'org.springframework.retry:spring-retry'
    implementation 'org.springframework.boot:spring-boot-starter-aop'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
    testImplementation 'org.springframework.security:spring-security-test'

    // lombok
    compileOnly 'org.projectlombok:lombok'
    annotationProcessor 'org.projectlombok:lombok'
    testCompileOnly 'org.projectlombok:lombok'
    testAnnotationProcessor 'org.projectlombok:lombok'

    // JUnit
    testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:${junit_jupiter_version}"
    testImplementation "org.junit.jupiter:junit-jupiter-api:${junit_jupiter_version}"

    // H2
    testImplementation "com.h2database:h2:${h2_version}"

    // WireMock
    testImplementation "com.github.tomakehurst:wiremock-jre8:${wiremock_version}"
  }

  tasks {
    withType(Test) {
      // Junit5有効化
      useJUnitPlatform()

      testLogging {
        // テスト時の標準出力と標準エラー出力を表示しない
        showStandardStreams false

        // イベントを出力 (TestLogEvent)
        events 'started', 'skipped', 'passed', 'failed'

        // 例外発生時の出力設定 (TestExceptionFormat)
        //exceptionFormat 'full'
      }
      systemProperty "file.encoding", "UTF-8"
    }
  }
}

パターン②

サブプロジェクトで共通の設定を独自プラグインjava-common.gradle)に記載しているプロジェクト。

bb
 |
 |ーー buildSrc
 |           |ーー build.gradle 
 |           |ーー src/main/groovy
 |           |           |ーーjava-common.gradle
 |
 |ーー bb-common
 |           |ーー build.gradle
 |
 |ーー bb-webapp
 |           |ーー build.gradle
 |
 |ーー bb-functions
 |           |ーー build.gradle
 |
 |ーー build.gradle

bb/buildSrc/src/main/groovy/java-common.gradle

plugins {
  id 'java'
  id 'eclipse'
  id 'project-report'
  id 'org.springframework.boot'
  id 'io.spring.dependency-management'
}

// Javaのバージョン指定
sourceCompatibility = 17
targetCompatibility = 17

// コンパイル時のエンコーディング指定
[compileJava, compileTestJava]*.options*.encoding = "UTF-8"

// グループIDの設定
group = 'jp.co.sample.bb'

repositories {
  mavenCentral()
}

// コンパイル時にAnnotation Processor(lombok,Doma)を有効にする
configurations {
  compileOnly {
    extendsFrom annotationProcessor
  }
  testCompileOnly {
    extendsFrom testAnnotationProcessor
  }
}

dependencies {
  // Spring
  implementation 'org.springframework.boot:spring-boot-starter-web'
  implementation 'org.springframework.boot:spring-boot-starter-oauth2-client'
  implementation 'org.springframework.retry:spring-retry'
  implementation 'org.springframework.boot:spring-boot-starter-aop'
  testImplementation 'org.springframework.boot:spring-boot-starter-test'
  testImplementation 'org.springframework.security:spring-security-test'

  // lombok
  compileOnly 'org.projectlombok:lombok'
  annotationProcessor 'org.projectlombok:lombok'
  testCompileOnly 'org.projectlombok:lombok'
  testAnnotationProcessor 'org.projectlombok:lombok'

  // JUnit
  testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:5.9.1"
  testImplementation "org.junit.jupiter:junit-jupiter-api:5.9.1"

  // H2
  testImplementation "com.h2database:h2:2.1.214"

  // WireMock
  testImplementation "com.github.tomakehurst:wiremock-jre8:2.35.1"
}

tasks.named('test') {
  // Junit5有効化
  useJUnitPlatform()

  testLogging {
    // テスト時の標準出力と標準エラー出力を表示しない
    showStandardStreams false

    // イベントを出力 (TestLogEvent)
    events 'started', 'skipped', 'passed', 'failed'

    // 例外発生時の出力設定 (TestExceptionFormat)
    //exceptionFormat 'full'
  }
  systemProperty "file.encoding", "UTF-8"
}

bb/build.gradle

plugins {
    id 'java'
}

実装方法

スタンドアロンのプラグイン実装サンプルを基に、設定を追加していく。

パターン①

1. プラグインの追加

ルートプロジェクトにTest Report Aggregation Pluginを追加する。

aa/build.gradle

plugins {
  id 'java'
  id 'eclipse'
  id 'org.springframework.boot' version "2.7.18" apply false
  id 'io.spring.dependency-management' version "1.1.0" apply false
  id 'test-report-aggregation'
  // ↓追加
  id 'test-report-aggregation'
  // ↑追加
}

2. ユニットテストの結果を基にテストレポートを出力する

一括出力する対象のテストとしてユニットテストを指定するように設定を追加する。

aa/build.gradle

reporting {
  reports {
    testAggregateTestReport(AggregateTestReport) { 
      // Junitのテスト結果レポートを出力する
      testType = TestSuiteType.UNIT_TEST
    }
  }
}

3. テストレポート出力対象とするプロジェクトを指定する

ルートプロジェクトの依存関係に、テストレポート出力対象としたいサブプロジェクトを指定する。

aa/build.gradle

dependencies {
    // テストレポート出力対象プロジェクト
    testReportAggregation project(':aa-common')
    testReportAggregation project(':aa-functions')
    testReportAggregation project(':aa-webapp')
}

4. テストレポートの出力先を指定する

デフォルトでは、テストレポートはルートプロジェクトの「build\reports\tests\unit-test\aggregated-results」配下に出力される。
※ルートプロジェクトなのは、プラグインをルートプロジェクトに導入しているため

destinationDirectory」プロパティでテストレポートの出力場所を変更することができる。
例えば、以下のように設定した場合、ルートプロジェクトの「build\reports\aggregate」配下にテストレポートが出力される。

aa/build.gradle

testAggregateTestReport {
  // テストレポートの出力先を定義する
  destinationDirectory = reporting.baseDirectory.dir('aggregate')
}

5. 「check」タスクにレポート一括出力タスクを紐づける

4までの設定で、テストレポートを一括出力することができるようになるが、「build」タスクで実行されるどのタスクとも紐づいていないため、「build」タスクを実行してもテストレポートが出力されない。
ここでは、「check」タスクにテストレポート一括出力タスクを紐づけるための設定を追加する。

aa/build.gradle

// checkタスク実行時にテストレポートを出力する
tasks.named('check') {
  dependsOn tasks.named('testAggregateTestReport', TestReport) 
}

6. 個別のレポート出力をしないように設定する

ここまでの設定で、「build」タスク実行時にテストレポートが一括出力される。
各プロジェクト毎のテストレポートも同時に出力されてしまっているので、個別のレポートは不要であれば
サブプロジェクトの共通の設定として、テストレポートを出力しないように設定を追加する。

aa/build.gradle

subprojects {
  // ↓追加
  test {
    // テスト結果レポートは別タスクで全プロジェクトのものを一括で出力する
    reports.html.required = false
  }
  // ↑追加
}

7. ビルドする

「aa/build.gradle」は以下のようになっている想定。

plugins {
  id 'java'
  id 'eclipse'
  id 'org.springframework.boot' version "2.7.18" apply false
  id 'io.spring.dependency-management' version "1.1.0" apply false
  id 'test-report-aggregation'
}

jar {
  // ルートプロジェクトなのでjarを作成しない
  enabled = false
}

reporting {
  reports {
    testAggregateTestReport(AggregateTestReport) { 
      // Junitのテスト結果レポートを出力する
      testType = TestSuiteType.UNIT_TEST
    }
  }
}

dependencies {
  // テストレポート出力対象プロジェクト
  testReportAggregation project(':aa-common')
  testReportAggregation project(':aa-functions')
  testReportAggregation project(':aa-webapp')
}

testAggregateTestReport {
  // テストレポートの出力先を定義する
  destinationDirectory = reporting.baseDirectory.dir('aggregate')
}

// checkタスク実行時にテストレポートを出力する
tasks.named('check') {
  dependsOn tasks.named('testAggregateTestReport', TestReport) 
}

subprojects {
  apply plugin: 'java'
  apply plugin: 'java-library'
  apply plugin: "eclipse"
  apply plugin: 'project-report'
  apply plugin: 'io.spring.dependency-management'

  // バージョン
  version = '1.0.0'

  // Javaのバージョン指定
  java.sourceCompatibility = JavaLanguageVersion.of(17)
  java.targetCompatibility = JavaLanguageVersion.of(17)

  // コンパイル時のエンコーディング指定
  [compileJava, compileTestJava]*.options*.encoding = "UTF-8"

  repositories {
    mavenCentral()
  }

  // コンパイル時にAnnotation Processor(lombok)を有効にする
  configurations {
    compileOnly {
      extendsFrom annotationProcessor
    }
    testCompileOnly {
      extendsFrom testAnnotationProcessor
    }
  }

  dependencies {
    // Spring
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'org.springframework.boot:spring-boot-starter-oauth2-client'
    implementation 'org.springframework.retry:spring-retry'
    implementation 'org.springframework.boot:spring-boot-starter-aop'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
    testImplementation 'org.springframework.security:spring-security-test'

    // lombok
    compileOnly 'org.projectlombok:lombok'
    annotationProcessor 'org.projectlombok:lombok'
    testCompileOnly 'org.projectlombok:lombok'
    testAnnotationProcessor 'org.projectlombok:lombok'

    // JUnit
    testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:${junit_jupiter_version}"
    testImplementation "org.junit.jupiter:junit-jupiter-api:${junit_jupiter_version}"

    // H2
    testImplementation "com.h2database:h2:${h2_version}"

    // WireMock
    testImplementation "com.github.tomakehurst:wiremock-jre8:${wiremock_version}"
  }

  tasks {
    withType(Test) {
      // Junit5有効化
      useJUnitPlatform()

      testLogging {
        // テスト時の標準出力と標準エラー出力を表示しない
        showStandardStreams false

        // イベントを出力 (TestLogEvent)
        events 'started', 'skipped', 'passed', 'failed'

        // 例外発生時の出力設定 (TestExceptionFormat)
        //exceptionFormat 'full'
      }
      systemProperty "file.encoding", "UTF-8"
    }
  }

  test {
    // テスト結果レポートは別タスクで全プロジェクトのものを一括で出力する
    reports.html.required = false
  }
}

ビルドを実行すると、「build\reports\aggregate」配下にテストレポートが出力される。

gradle build

パターン②

1. プラグインの追加

ルートプロジェクトにTest Report Aggregation Pluginを追加する。

bb/build.gradle

plugins {
    id 'java'
    id 'eclipse'
    // ↓追加
    id 'test-report-aggregation'
    // ↑追加
}

2. ユニットテストの結果を基にテストレポートを出力する

一括出力する対象のテストとしてユニットテストを指定するように設定を追加する。

bb/build.gradle

reporting {
  reports {
    testAggregateTestReport(AggregateTestReport) { 
      // Junitのテスト結果レポートを出力する
      testType = TestSuiteType.UNIT_TEST
    }
  }
}

3. テストレポート出力対象とするプロジェクトを指定する

ルートプロジェクトの依存関係に、テストレポート出力対象としたいサブプロジェクトを指定する。

bb/build.gradle

dependencies {
    // テストレポート出力対象プロジェクト
    testReportAggregation project(':bb-common')
    testReportAggregation project(':bb-functions')
    testReportAggregation project(':bb-webapp')
}

4. テストレポートの出力先を指定する

デフォルトでは、テストレポートはルートプロジェクトの「build\reports\tests\unit-test\aggregated-results」配下に出力される。
※ルートプロジェクトなのは、プラグインをルートプロジェクトに導入しているため

destinationDirectory」プロパティでテストレポートの出力場所を変更することができる。
例えば、以下のように設定した場合、ルートプロジェクトの「build\reports\aggregate」配下にテストレポートが出力される。

aa/build.gradle

testAggregateTestReport {
  // テストレポートの出力先を定義する
  destinationDirectory = reporting.baseDirectory.dir('aggregate')
}

5. 「check」タスクにレポート一括出力タスクを紐づける

4までの設定で、テストレポートを一括出力することができるようになるが、「build」タスクで実行されるどのタスクとも紐づいていないため、「build」タスクを実行してもテストレポートが出力されない。 ここでは、「check」タスクにテストレポート一括出力タスクを紐づけるための設定を追加する。

bb/build.gradle

// checkタスク実行時にテストレポートを出力する
tasks.named('check') {
  dependsOn tasks.named('testAggregateTestReport', TestReport) 
}

6. 個別のレポート出力をしないように設定する

ここまでの設定で、「build」タスク実行時にテストレポートが一括出力される。
各プロジェクト毎のテストレポートも同時に出力されてしまっているので、個別のレポートは不要であれば
独自プラグインの設定として、テストレポートを出力しないように設定を追加する。

bb/buildSrc/src/main/groovy/java-common.gradle

// ↓追加
test {
  // テスト結果レポートは別タスクで全プロジェクトのものを一括で出力する
  reports.html.required = false
}
// ↑追加

7. ビルドする

「bb/build.gradle」は以下のようになっている想定。

plugins {
  id 'java'
  id 'test-report-aggregation'
}

reporting {
  reports {
    testAggregateTestReport(AggregateTestReport) { 
      // Junitのテスト結果レポートを出力する
      testType = TestSuiteType.UNIT_TEST
    }
  }
}

dependencies {
  // テストレポート出力対象プロジェクト
  testReportAggregation project(':bb-common')
  testReportAggregation project(':bb-functions')
  testReportAggregation project(':bb-webapp')
}

testAggregateTestReport {
  // テストレポートの出力先を定義する
  destinationDirectory = reporting.baseDirectory.dir('aggregate')
}

// checkタスク実行時にテストレポートを出力する
tasks.named('check') {
  dependsOn tasks.named('testAggregateTestReport', TestReport) 
}

ビルドを実行すると、「build\reports\aggregate」配下にテストレポートが出力される。

gradle build