かんがるーさんの日記

最近自分が興味をもったものを調べた時の手順等を書いています。今は Spring Boot をいじっています。

Spring Boot 2.4.x の Web アプリを 2.5.x へバージョンアップする ( その5 )( Spock を 1.3-groovy-2.5 → 2.0-groovy-3.0 へバージョンアップする )

概要

記事一覧はこちらです。

Spring Boot 2.4.x の Web アプリを 2.5.x へバージョンアップする ( その4 )( Gradle を 6.9.1 → 7.2 へバージョンアップする ) の続きです。

  • 今回の手順で確認できるのは以下の内容です。
    • Spock を 1.3-groovy-2.5 → 2.0-groovy-3.0 へバージョンアップします。
    • Spock を 2系にバージョンアップすると JUnit 5 ベースになるので、PowerMock で作成している static method のテストを Mockito で書き直して PowerMock を依存関係から削除します。

参照したサイト・書籍

  1. Spock
    https://spockframework.org/

  2. Spock - Migration Guide
    https://spockframework.org/spock/docs/2.0/migration_guide.html

  3. Apache Groovy
    https://groovy-lang.org/index.html

  4. Groovy Language Documentation Version 3.0.8
    http://docs.groovy-lang.org/docs/groovy-3.0.8/html/documentation/

  5. Junit5 mock a static method
    https://stackoverflow.com/questions/52830441/junit5-mock-a-static-method

目次

  1. Spock を 1.3-groovy-2.5 → 2.0-groovy-3.0 へバージョンアップする
  2. PowerMock で作成している static method のテストを Mockito で書き直して PowerMock を依存関係から削除する
  3. @Unroll アノテーションを削除する

手順

Spock を 1.3-groovy-2.5 → 2.0-groovy-3.0 へバージョンアップする

build.gradle の以下の点を変更します。

dependencyManagement {
    imports {
        // bomProperty に指定可能な property は以下の URL の BOM に記述がある
        // https://repo1.maven.org/maven2/org/springframework/boot/spring-boot-dependencies/2.5.4/spring-boot-dependencies-2.5.4.pom
        mavenBom(org.springframework.boot.gradle.plugin.SpringBootPlugin.BOM_COORDINATES) {
            // Spring Boot の BOM に定義されているバージョンから変更する場合には、ここに以下のように記述する
            // bomProperty "thymeleaf.version", "3.0.9.RELEASE"
        }
        mavenBom("org.junit:junit-bom:5.7.2")
    }
}

dependencies {
    def spockVersion = "2.0-groovy-3.0"
    ..........
}

..........

clean {
    doLast {
        rootProject.file("src/main/generated").deleteDir()
        rootProject.file("src/test/generated_tests").deleteDir()
    }
}

test {
    jvmArgs = jvmArgsForTask +
            jvmArgsAddOpens +
            ["-Dspring.profiles.active=unittest"]

    // for JUnit 5
    useJUnitPlatform()

    testLogging {
        afterSuite printTestCount
    }
}

..........
  • dependencyManagement block の以下の点を変更します。
    • bomProperty "groovy.version", "2.5.12" を削除します。これにより groovy のバージョンが 3.0.8 になります(Spring Boot 2.5.4 の Dependency Versionsorg.codehaus.groovy:groovy 参照)。
  • dependencies block の以下の点を変更します。
    • def spockVersion = "1.3-groovy-2.5"def spockVersion = "2.0-groovy-3.0"
  • テストが全て Junit 5 ベースになることにより testJUnit4AndSpock タスクが不要になるので、以下の記述を削除します。
    • task testJUnit4AndSpock(type: Test) { ... }
    • test.dependsOn testJUnit4AndSpock

Gradle Tool Window の左上にある「Refresh all Gradle projects」ボタンをクリックして更新します。

clean タスク実行 → Rebuild Project 実行をすると、Groovyc: unable to resolve class groovy.sql.Sql というエラーが出力されました。

f:id:ksby:20210829170823p:plain

src/test/groovy/ksbysample/webapp/lending/SampleTest.groovy を見ると groovy.sql.Sql が赤く表示されており package が存在しないようです。

f:id:ksby:20210829170957p:plain

Groovy Language Documentation Version 3.0.8 を見ると 3.8. Interacting with a SQL database に groovy-sql module が必要であるとの記述がありました。 build.gradle の dependencies block に testImplementation("org.codehaus.groovy:groovy-sql") を追加します。

dependencies {
    ..........

    // dependency-management-plugin によりバージョン番号が自動で設定されるもの
    // Appendix F. Dependency versions ( https://docs.spring.io/spring-boot/docs/current/reference/html/appendix-dependency-versions.html ) 参照
    ..........
    testImplementation("org.yaml:snakeyaml")
    testImplementation("org.codehaus.groovy:groovy-sql")

    ..........

clean タスク実行 → Rebuild Project 実行 → build タスクを実行すると "BUILD SUCCESSFUL" のメッセージが出力されました。

f:id:ksby:20210829172230p:plain

PowerMock で作成している static method のテストを Mockito で書き直して PowerMock を依存関係から削除する

src/test/groovy/ksbysample/webapp/lending/SampleHelperTest.groovy に記述した PowerMock で作成している static method のテストを、

package ksbysample.webapp.lending

import org.junit.Test
import org.junit.experimental.runners.Enclosed
import org.junit.runner.RunWith
import org.mockito.Mockito
import org.powermock.api.mockito.PowerMockito
import org.powermock.core.classloader.annotations.PowerMockIgnore
import org.powermock.core.classloader.annotations.PrepareForTest
import org.powermock.modules.junit4.PowerMockRunner
import org.powermock.modules.junit4.PowerMockRunnerDelegate
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.test.context.junit4.SpringRunner
import spock.lang.Specification
import spock.lang.Unroll

import javax.crypto.NoSuchPaddingException

import static org.assertj.core.api.Assertions.assertThatExceptionOfType

@RunWith(Enclosed)
class SampleHelperTest {

    ..........

    @RunWith(PowerMockRunner)
    @PowerMockRunnerDelegate(SpringRunner)
    @SpringBootTest
    @PrepareForTest(BrowfishUtils)
    @PowerMockIgnore(["javax.management.*", "com.sun.org.apache.xerces.*", "javax.xml.*", "org.xml.*", "org.w3c.dom.*"])
    static class 異常処理のテスト {

        @Autowired
        private SampleHelper sampleHelper

        @Test
        void "SampleHelper_encryptを呼ぶとRuntimeExceptionをthrowする"() {
            // setup:
            PowerMockito.mockStatic(BrowfishUtils)
            PowerMockito.when(BrowfishUtils.encrypt(Mockito.any()))
                    .thenThrow(new NoSuchPaddingException())

            // expect:
            assertThatExceptionOfType(RuntimeException).isThrownBy({
                sampleHelper.encrypt("test")
            })
        }

    }

}

以下のように変更します。

package ksbysample.webapp.lending

import org.junit.experimental.runners.Enclosed
import org.junit.jupiter.api.Test
import org.junit.runner.RunWith
import org.mockito.Mockito
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
import spock.lang.Specification
import spock.lang.Unroll

import javax.crypto.NoSuchPaddingException

import static org.assertj.core.api.Assertions.assertThatThrownBy

@RunWith(Enclosed)
class SampleHelperTest {

    ..........

    @SpringBootTest
    static class 異常処理のテスト {

        @Autowired
        private SampleHelper sampleHelper

        @Test
        void "SampleHelper_encryptを呼ぶとRuntimeExceptionをthrowする"() {
            // setup:
            Mockito.mockStatic(BrowfishUtils)
            Mockito.when(BrowfishUtils.encrypt(Mockito.any()))
                    .thenThrow(new NoSuchPaddingException())

            // expect:
            assertThatThrownBy(() -> {
                sampleHelper.encrypt("test")
            }).isInstanceOf(RuntimeException)
        }

    }

}

Mockito で static method のテストを実装するには mockito-inline が必要なので、build.gradle の以下の点を変更します。

dependencies {
    ..........

    // dependency-management-plugin によりバージョン番号が自動で設定されるもの
    // Appendix F. Dependency versions ( https://docs.spring.io/spring-boot/docs/current/reference/html/appendix-dependency-versions.html ) 参照
    ..........
    testImplementation("org.codehaus.groovy:groovy-sql")
    testImplementation("org.mockito:mockito-inline")
  • dependencies から以下の行を削除して PowerMock を依存関係から削除します。
    • def powermockVersion = "2.0.9"
    • testImplementation("org.powermock:powermock-module-junit4:${powermockVersion}")
    • testImplementation("org.powermock:powermock-api-mockito2:${powermockVersion}")
  • testImplementation("org.mockito:mockito-inline") を追加します。

Gradle Tool Window の左上にある「Refresh all Gradle projects」ボタンをクリックして更新します。

変更したテストを実行すると無事成功しました。

f:id:ksby:20210829182824p:plain

@Unroll アノテーションを削除する

Spock の Migration GuideUnroll changes を読むと @Unroll アノテーションがなくても Data Driven テストが動作するようになったとのことなので、@Unroll アノテーションを削除します。

削除後に clean タスク実行 → Rebuild Project 実行 → build タスクを実行すると "BUILD SUCCESSFUL" のメッセージが出力されて、テスト数も削除前と同じでした。

f:id:ksby:20210829211838p:plain

履歴

2021/08/29
初版発行。