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

プラグインを使用しないテストレポート一括出力のサンプルとして、 下記の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'
}

実装方法

Test reportingに記載されている方法で実装する。

パターン①

1. プロジェクト全体でテスト結果を共有する設定の追加

サブプロジェクトの共通の設定として、プロジェクト全体でテストレポートのデータを共有するための設定を追加する。

aa/build.gradle

subprojects {
  // ↓追加
  // プロジェクト全体で集計するテストレポートデータを共有するための設定
  configurations {
    binaryTestResultsElements {
      canBeResolved = false
      canBeConsumed = true
      attributes {
        attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category, Category.DOCUMENTATION))
        attribute(DocsType.DOCS_TYPE_ATTRIBUTE, objects.named(DocsType, 'test-report-data'))
      }
      outgoing.artifact(test.binaryResultsDirectory)
    }
  }
  // ↑追加
}

2. 共有されたテスト結果を取得する設定の追加

ルートプロジェクトの設定として、共有されたサブプロジェクトのテストレポートデータを取得するための設定を追加する。

aa/build.gradle

// ↓追加
// テストレポートデータを収集するための設定
configurations {
  testReportData {
    canBeResolved = true
    canBeConsumed = false
    attributes {
      attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category, Category.DOCUMENTATION))
      attribute(DocsType.DOCS_TYPE_ATTRIBUTE, objects.named(DocsType, 'test-report-data'))
    }
  }
}
// ↑追加

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

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

aa/build.gradle

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

4. テストレポート出力タスクを定義する

ルートプロジェクトにテストレポートを一括出力するためのタスクを新規で定義する。
その際、テストレポートの出力先と、テストの結果の取得先を指定する。

以下のように指定した場合、テストレポートはルートプロジェクトの「build/reports/allTests」配下に出力される。

aa/build.gradle

// ↓追加
// テスト結果レポートを作成する場合は以下のタスクを実行する
// gradlew testReport
tasks.register('testReport', TestReport) {
  // テストレポートの出力先
  destinationDirectory = reporting.baseDirectory.dir('allTests')
  // テスト結果の取得先
  testResults.from(configurations.testReportData)
}
// ↑追加

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

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

aa/build.gradle

// checkタスク実行時にテストレポートを出力する
tasks.named('check') {
  dependsOn tasks.named('testReport', 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 "${spring_boot_version}" apply false
  id 'io.spring.dependency-management' version "${spring_dependency_management_version}" apply false
}

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

// テストレポートデータを収集するための設定
configurations {
  testReportData {
    canBeResolved = true
    canBeConsumed = false
    attributes {
      attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category, Category.DOCUMENTATION))
      attribute(DocsType.DOCS_TYPE_ATTRIBUTE, objects.named(DocsType, 'test-report-data'))
    }
  }
}

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

// テスト結果レポートを作成する場合は以下のタスクを実行する
// gradlew testReport
tasks.register('testReport', TestReport) {
  destinationDirectory = reporting.baseDirectory.dir('allTests')
  testResults.from(configurations.testReportData)
}

// checkタスク実行時にテストレポートを出力する
tasks.named('check') {
  dependsOn tasks.named('testReport', 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"
    }
  }

  // プロジェクト全体で集計するテストレポートデータを共有するための設定
  configurations {
    binaryTestResultsElements {
      canBeResolved = false
      canBeConsumed = true
      attributes {
        attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category, Category.DOCUMENTATION))
        attribute(DocsType.DOCS_TYPE_ATTRIBUTE, objects.named(DocsType, 'test-report-data'))
      }
      outgoing.artifact(test.binaryResultsDirectory)
    }
  }

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

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

gradle build

パターン②

1. プロジェクト全体でテスト結果を共有する設定の追加

独自プラグインの設定に、プロジェクト全体でテストレポートのデータを共有するための設定を追加する。

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

// ↓追加
// プロジェクト全体で集計するテストレポートデータを共有するための設定
configurations {
  binaryTestResultsElements {
    canBeResolved = false
    canBeConsumed = true
    attributes {
      attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category, Category.DOCUMENTATION))
      attribute(DocsType.DOCS_TYPE_ATTRIBUTE, objects.named(DocsType, 'test-report-data'))
    }
    outgoing.artifact(test.binaryResultsDirectory)
  }
}
// ↑追加

2. 共有されたテスト結果を取得する設定の追加

ルートプロジェクトの設定として、共有されたサブプロジェクトのテストレポートデータを取得するための設定を追加する。

bb/build.gradle

// ↓追加
// テストレポートデータを収集するための設定
configurations {
  testReportData {
    canBeResolved = true
    canBeConsumed = false
    attributes {
      attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category, Category.DOCUMENTATION))
      attribute(DocsType.DOCS_TYPE_ATTRIBUTE, objects.named(DocsType, 'test-report-data'))
    }
  }
}
// ↑追加

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

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

bb/build.gradle

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

4. テストレポート出力タスクを定義する

ルートプロジェクトにテストレポートを一括出力するためのタスクを新規で定義する。
その際、テストレポートの出力先と、テストの結果の取得先を指定する。

以下のように指定した場合、テストレポートはルートプロジェクトの「build/reports/allTests」配下に出力される。

bb/build.gradle

// ↓追加
// テスト結果レポートを作成する場合は以下のタスクを実行する
// gradlew testReport
tasks.register('testReport', TestReport) {
  // テストレポートの出力先
  destinationDirectory = reporting.baseDirectory.dir('allTests')
  // テスト結果の取得先
  testResults.from(configurations.testReportData)
}
// ↑追加

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

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

bb/build.gradle

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

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

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

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

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

7. ビルドする

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

plugins {
  id 'java'
}

// テストレポートデータを収集するための設定
configurations {
  testReportData {
    canBeResolved = true
    canBeConsumed = false
    attributes {
      attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category, Category.DOCUMENTATION))
      attribute(DocsType.DOCS_TYPE_ATTRIBUTE, objects.named(DocsType, 'test-report-data'))
    }
  }
}

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

// テスト結果レポートを作成する場合は以下のタスクを実行する
// gradlew testReport
tasks.register('testReport', TestReport) {
  destinationDirectory = reporting.baseDirectory.dir('allTests')
  testResults.from(configurations.testReportData)
}

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

「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"
}

// プロジェクト全体で集計するテストレポートデータを共有するための設定
configurations {
  binaryTestResultsElements {
    canBeResolved = false
    canBeConsumed = true
    attributes {
      attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category, Category.DOCUMENTATION))
      attribute(DocsType.DOCS_TYPE_ATTRIBUTE, objects.named(DocsType, 'test-report-data'))
    }
    outgoing.artifact(test.binaryResultsDirectory)
  }
}

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

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

gradle build