かんがるーさんの日記

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

Spring Boot + npm + Geb で入力フォームを作ってテストする ( その99 )( Gradle を 6.9.1 → 7.2 へ、Spring Boot を 2.4.10 → 2.5.4 へ、Geb を 4.1 → 5.0 へバージョンアップする )

概要

記事一覧はこちらです。

Spring Boot + npm + Geb で入力フォームを作ってテストする ( その98 )( Gradle を 6.4 → 6.9.1 へ、Spring Boot を 2.2.7 → 2.4.10 へ、Geb を 3.4 → 4.1 へバージョンアップする ) の続きです。

  • 今回の手順で確認できるのは以下の内容です。
    • Gradle を 6.9.1 → 7.2 へバージョンアップします。
    • Spring Boot を 2.4.10 → 2.5.4 へ、Geb を 4.1 → 5.0 へバージョンアップします。
    • 今回だけでは完了しなかったので、2回に分けます。

参照したサイト・書籍

  1. erdi / webdriver-binaries-gradle-plugin
    https://github.com/erdi/webdriver-binaries-gradle-plugin

目次

  1. Gradle を 6.9.1 → 7.2 へバージョンアップする
  2. Spring Boot を 2.4.10 → 2.5.4 へ、Geb を 4.1 → 5.0 へバージョンアップする。。。がエラーが出てバージョンアップできず
  3. configureChromeDriverBinary タスクでエラーになる原因を調査した結果、Gradle Plugin を com.energizedwork.webdriver-binaries → com.github.erdi.webdriver-binaries に切り替える
  4. test タスクで実行されるテストから src/test/groovy/geb/ の下のテストを除外する
  5. org.junit.Test アノテーションが付いているテストを org.junit.jupiter.api.Test アノテーションに変更する
  6. 続く。。。

手順

Gradle を 6.9.1 → 7.2 へバージョンアップする

build.gradle の wrapper タスクの記述を以下のように変更します。

wrapper {
    gradleVersion = "7.2"
    distributionType = Wrapper.DistributionType.ALL
}
  • gradleVersion = "6.9.1"gradleVersion = "7.2" に変更します。

コマンドプロンプトから gradlew wrapper --gradle-version=7.2gradlew --version コマンドを実行します。gradlew --stop で Gradle Deamon を止めただけでは java.lang.IllegalArgumentException: Unsupported class file major version 61 のエラーメッセージが出たので、PC を再起動してからコマンドを実行しています。

f:id:ksby:20211010092309p:plain

gradle/wrapper/gradle-wrapper.properties は以下の内容になります。

distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-all.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

Spring Boot 2.4.x の Web アプリを 2.5.x へバージョンアップする ( その4 )( Gradle を 6.9.1 → 7.2 へバージョンアップする ) で実施した変更内容を build.gradle に反映します。

tasks.named("compileTestJava").configure {
    options.errorprone.enabled = false
}
bootJar {
    duplicatesStrategy = DuplicatesStrategy.INCLUDE
}
jar {
    enabled = false
}

// for Doma 2
// Copy the resources referred by the Doma annotation processors to
// the destinationDir of the compileJava task
task copyDomaResources(type: Sync) {
    from sourceSets.main.resources.srcDirs
    into compileJava.destinationDirectory
    include "doma.compile.config"
    include "META-INF/**/*.sql"
    include "META-INF/**/*.script"
}
compileJava.dependsOn copyDomaResources
  • bootJar { duplicatesStrategy = DuplicatesStrategy.INCLUDE } を追加します。
  • jar { enabled = false } を追加します。
  • copyDomaResources タスクの以下の点を変更します。
    • into compileJava.destinationDirinto compileJava.destinationDirectory

Gradle Tool Window の左上にある「Refresh all Gradle projects」ボタンをクリックして更新します。。。が、Could not find method testCompile() のエラーメッセージが表示されました。

f:id:ksby:20211010093406p:plain

古い文法のままでした。。。 その下の testRuntime も testRuntimeOnly に変更されているので、build.gradle を以下のように変更します。

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

    // for JUnit 5
    // junit-jupiter で junit-jupiter-api, junit-jupiter-params, junit-jupiter-engine の3つが依存関係に追加される
    testImplementation("org.junit.jupiter:junit-jupiter")
    testRuntimeOnly("org.junit.platform:junit-platform-launcher")
}
  • testCompile("org.junit.jupiter:junit-jupiter")testImplementation("org.junit.jupiter:junit-jupiter") に変更します。
  • testRuntime("org.junit.platform:junit-platform-launcher")testRuntimeOnly("org.junit.platform:junit-platform-launcher") に変更します。

再度 Gradle Tool Window の左上にある「Refresh all Gradle projects」ボタンをクリックして更新すると、今度は問題なく終了しました。

Spring Boot を 2.4.10 → 2.5.4 へ、Geb を 4.1 → 5.0 へバージョンアップする。。。がエラーが出てバージョンアップできず

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

buildscript {
    ext {
        group "ksbysample"
        version "2.5.4"
    }
    repositories {
        mavenCentral()
        gradlePluginPortal()
    }
    dependencies {
        // for doma-codegen-plugin
        classpath "com.h2database:h2:1.4.200"
    }
}

plugins {
    id "java"
    id "groovy"
    id "eclipse"
    id "idea"
    id "org.springframework.boot" version "2.5.4"
    id "io.spring.dependency-management" version "1.0.11.RELEASE"
    id "net.ltgt.errorprone" version "1.1.1"
    id "checkstyle"
    id "com.github.spotbugs" version "4.0.8"
    id "pmd"
    id "com.github.node-gradle.node" version "3.1.1"
    id "com.gorylenko.gradle-git-properties" version "2.3.1"
    id "com.energizedwork.webdriver-binaries" version "1.4"
    id "org.seasar.doma.codegen" version "1.4.1"
}

..........

dependencyManagement {
    imports {
        // mavenBom は以下の URL のものを使用する
        // https://repo.spring.io/release/org/springframework/boot/spring-boot-starter-parent/2.1.4.RELEASE/
        // bomProperty に指定可能な property は以下の URL の BOM に記述がある
        // https://repo.spring.io/release/org/springframework/boot/spring-boot-dependencies/2.1.4.RELEASE/spring-boot-dependencies-2.1.4.RELEASE.pom
        mavenBom(org.springframework.boot.gradle.plugin.SpringBootPlugin.BOM_COORDINATES)
        mavenBom("org.junit:junit-bom:5.8.1")
    }
}

dependencies {
    def spockVersion = "2.0-groovy-3.0"
    def lombokVersion = "1.18.20"
    def domaVersion = "2.49.0"
    def errorproneVersion = "2.3.4"
    def seleniumVersion = "3.141.59"
    def spotbugsVersion = "4.0.2"

    // dependency-management-plugin によりバージョン番号が自動で設定されるもの
    // Appendix F. Dependency versions ( https://docs.spring.io/spring-boot/docs/2.1.4.RELEASE/reference/html/appendix-dependency-versions.html ) 参照
    implementation("org.springframework.boot:spring-boot-starter-web")
    implementation("org.springframework.boot:spring-boot-starter-validation")
    implementation("org.springframework.boot:spring-boot-starter-thymeleaf")
    implementation("org.springframework.boot:spring-boot-starter-data-jpa")
    implementation("org.springframework.boot:spring-boot-starter-freemarker")
    implementation("org.springframework.boot:spring-boot-starter-mail")
    implementation("org.springframework.boot:spring-boot-starter-security")
    implementation("org.springframework.boot:spring-boot-starter-actuator")
    runtimeOnly("org.springframework.boot:spring-boot-devtools")
    implementation("org.springframework.session:spring-session-core")
    implementation("org.springframework.session:spring-session-jdbc")
    implementation("org.codehaus.janino:janino")
    testImplementation("org.springframework.boot:spring-boot-starter-test") {
        exclude group: "org.junit.vintage", module: "junit-vintage-engine"
    }
    testImplementation("org.springframework.security:spring-security-test")
    testImplementation("org.yaml:snakeyaml")
    testImplementation("org.codehaus.groovy:groovy-sql")

    // dependency-management-plugin によりバージョン番号が自動で設定されないもの、あるいは最新バージョンを指定したいもの
    implementation("com.integralblue:log4jdbc-spring-boot-starter:2.0.0")
    implementation("org.flywaydb:flyway-core:7.14.1")
    implementation("com.h2database:h2:1.4.200")
    implementation("com.github.rozidan:modelmapper-spring-boot-starter:2.3.1")
    implementation("com.google.guava:guava:31.0.1-jre")
    implementation("org.apache.commons:commons-lang3:3.12.0")
    testImplementation("org.dbunit:dbunit:2.7.2")
    testImplementation("org.assertj:assertj-core:3.21.0")
    testImplementation("org.spockframework:spock-core:${spockVersion}")
    testImplementation("org.spockframework:spock-spring:${spockVersion}")
    testImplementation("org.jsoup:jsoup:1.14.3")
    testImplementation("com.icegreen:greenmail:1.6.5")

    // for lombok
    annotationProcessor("org.projectlombok:lombok:${lombokVersion}")
    compileOnly("org.projectlombok:lombok:${lombokVersion}")

    // for Doma
    implementation("org.seasar.doma:doma-core:${domaVersion}")
    annotationProcessor("org.seasar.doma:doma-processor:${domaVersion}")

    // for Error Prone ( http://errorprone.info/ )
    errorprone("com.google.errorprone:error_prone_core:${errorproneVersion}")
    compileOnly("com.google.errorprone:error_prone_annotations:${errorproneVersion}")

    // PowerMock
    testImplementation("org.powermock:powermock-module-junit4:${powermockVersion}")
    testImplementation("org.powermock:powermock-api-mockito2:${powermockVersion}")

    // for Geb + Spock
    testImplementation("org.gebish:geb-spock:5.0")
    testImplementation("org.seleniumhq.selenium:selenium-chrome-driver:${seleniumVersion}")
    testImplementation("org.seleniumhq.selenium:selenium-firefox-driver:${seleniumVersion}")
    testImplementation("org.seleniumhq.selenium:selenium-support:${seleniumVersion}")

    // for SpotBugs
    compileOnly("com.github.spotbugs:spotbugs:${spotbugsVersion}")
    compileOnly("net.jcip:jcip-annotations:1.0")
    compileOnly("com.github.spotbugs:spotbugs-annotations:${spotbugsVersion}")
    testImplementation("com.google.code.findbugs:jsr305:3.0.2")
    spotbugsStylesheets("com.github.spotbugs:spotbugs:${spotbugsVersion}")
    spotbugsPlugins("com.h3xstream.findsecbugs:findsecbugs-plugin:1.10.1")

    // for JUnit 5
    // junit-jupiter で junit-jupiter-api, junit-jupiter-params, junit-jupiter-engine の3つが依存関係に追加される
    testImplementation("org.junit.jupiter:junit-jupiter")
    testRuntimeOnly("org.junit.platform:junit-platform-launcher")
}

..........

bootRun {
    jvmArgs = jvmArgsForTask +
            [
                    "-Dspring.profiles.active=develop",
                    "-XX:TieredStopAtLevel=1"
            ]
}

tasks.withType(Test) {
    jvmArgs = jvmArgsForTask +
            ["-Dspring.profiles.active=unittest"]
}
test {
    // test タスクの jvmArgs は tasks.withType(Test) { ... } で定義している

    // for JUnit 5
    useJUnitPlatform()

    testLogging {
        afterSuite printTestCount
    }
}

..........

Spring Boot 2.5.4 へのバージョンアップとして以下の点を変更します。

  • buildscript block の以下の点を変更します。
    • version "2.4.10"version "2.5.4"
  • plugins block の以下の点を変更します。
    • id "org.springframework.boot" version "2.4.10"id "org.springframework.boot" version "2.5.4"
  • dependencyManagement block の以下の点を変更します。
    • mavenBom(org.springframework.boot.gradle.plugin.SpringBootPlugin.BOM_COORDINATES) { bomProperty "groovy.version", "2.5.15" }mavenBom(org.springframework.boot.gradle.plugin.SpringBootPlugin.BOM_COORDINATES)

各種ライブラリのバージョンアップとして以下の点を変更します。

  • dependencyManagement block の以下の点を変更します。
    • mavenBom("org.junit:junit-bom:5.7.2")mavenBom("org.junit:junit-bom:5.8.1")
  • dependencies block の以下の点を変更します。
    • def spockVersion = "1.3-groovy-2.5"def spockVersion = "2.0-groovy-3.0"
    • testImplementation("org.codehaus.groovy:groovy-sql") を追加します。
    • testImplementation("org.gebish:geb-spock:4.1") { exclude group: "org.codehaus.groovy", module: "groovy-all" }testImplementation("org.gebish:geb-spock:5.0")
    • https://gebish.org/manual/current/#installation-usage を見ると selenium-apiselenium-remote-driver の2つは必要ないようなので、以下の2行を削除します。
      • testImplementation("org.seleniumhq.selenium:selenium-api:${seleniumVersion}")
      • testImplementation("org.seleniumhq.selenium:selenium-remote-driver:${seleniumVersion}")
    • powermock を依存関係から削除するため、以下の3行を削除します。
      • def powermockVersion = "2.0.7"
      • testImplementation("org.powermock:powermock-module-junit4:${powermockVersion}")
      • testImplementation("org.powermock:powermock-api-mockito2:${powermockVersion}")
  • def jvmArgsAddOpens = [ ... ] を削除します。
  • テストが全て JUnit 5 ベースになるので、testJUnit4AndSpock タスクを削除します。
    • task testJUnit4AndSpock(type: Test) { ... }
    • test.dependsOn testJUnit4AndSpock

ksbysample/webapp/bootnpmgeb/web/inquiry/form/InquiryInput02FormValidatorTest.groovy に powermock を利用したテストが記述されていますが、このままでは build が通らないので対象のテストをコメントアウトします(後で実装し直します)。

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

clean タスク実行 → Rebuild Project 実行 → build タスクを実行すると、configureChromeDriverBinary タスクでエラーになりました。。。

f:id:ksby:20211010184036p:plain

configureChromeDriverBinary タスクでエラーになる原因を調査した結果、Gradle Plugin を com.energizedwork.webdriver-binaries → com.github.erdi.webdriver-binaries に切り替える

gradlew configureChromeDriverBinary --stacktrace コマンドを実行してみると Caused by: java.lang.NoSuchMethodError: 'void org.gradle.wrapper.PathAssembler.<init>(java.io.File)' のエラーメッセージが出力されましたが、これでは原因がよく分かりません。

f:id:ksby:20211010184417p:plain

com.energizedwork.webdriver-binariesGoogle で検索したところ、erdi / webdriver-binaries-gradle-plugin のページにたどり着きました。新しい Gradle Plugin が出ているようなので、こちらに切り替えます。

plugins {
    ..........
    id "com.github.erdi.webdriver-binaries" version "2.6"
    ..........
}

..........

webdriverBinaries {
    chromedriver {
        version = "94.0.4606.41"
        architecture = "X86"
        fallbackTo32Bit = true
    }
    geckodriver {
        version = "0.29.1"
        architecture = "X86_64"
    }
}

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

clean タスク実行 → Rebuild Project 実行 → build タスクを実行すると、configureChromeDriverBinary タスクは正常終了しましたが test タスクでエラーが発生しました。

f:id:ksby:20211010190535p:plain

test タスクで実行されるテストから src/test/groovy/geb/ の下のテストを除外する

エラーになったテスト 13個は全て src/test/groovy/geb/gebspec/inquiry/InquiryTestSpec.groovy のテストでした。Spock、Geb がバージョンアップされたことに伴い JUnit 5 ベースのテストに変わったので、test タスクの実行対象になったことが原因です。Geb のテストを test タスクで実行する必要はないので除外します。

build.gradle の test タスクに exclude "geb/**" を追加します。

test {
    // test タスクの jvmArgs は tasks.withType(Test) { ... } で定義している

    // Geb のテストを test タスクで実行されないようにする
    exclude "geb/**"

    // for JUnit 5
    useJUnitPlatform()

    testLogging {
        afterSuite printTestCount
    }
}

また src/test/groovy/geb/gebspec/inquiry/InquiryTestSpec.groovy を見て気づいたのですが、JUnit 4 のクラスである org.junit.Rule の色がグレーになっていませんでした。build.gradle に記述されているモジュールの依存関係にまだ JUnit 4 が残っているようなので、後で JUnit 4 を依存関係から取り除くようにします。

f:id:ksby:20211010191414p:plain

clean タスク実行 → Rebuild Project 実行 → build タスクを実行すると "BUILD SUCCESSFUL" のメッセージが出力されましたが、成功したテストの件数が 147 → 116 と大きく減っていました。おそらく org.junit.Test アノテーションが付いているテストがあって、JUnit 5 から認識されないことが原因でしょう。

f:id:ksby:20211010192113p:plain

org.junit.Test アノテーションが付いているテストを org.junit.jupiter.api.Test アノテーションに変更する

org.junit.Test で検索すると以下の2つのファイルがヒットしました。というより Spock ベースで作成していないテストが全て JUnit 4 ベースで実装されていました。。。

  • src/test/groovy/ksbysample/webapp/bootnpmgeb/web/inquiry/InquiryCompleteControllerTest.groovy
  • src/test/groovy/ksbysample/webapp/bootnpmgeb/web/inquiry/InquiryInputControllerTest.groovy

まず GreenMail の起動・停止に使用している src/test/java/ksbysample/common/test/rule/mail/MailServerResource.javaJUnit 4 の org.junit.rules.ExternalResource を継承して作成されているので、JUnit 5 でも動作するよう変更します。

src/test/java/ksbysample/common/test の下に extension.mail パッケージを作成した後、MailServerExtension.java を新規作成して以下の内容を記述します。最初 BeforeEachCallback, AfterEachCallback インターフェースを実装して自動で起動・停止させようと思ったのですが、Spock では @RegisterExtension を付与しても自動起動・停止しなかったので止めました。

package ksbysample.common.test.extension.mail;

import com.icegreen.greenmail.util.GreenMail;
import com.icegreen.greenmail.util.ServerSetup;
import org.springframework.stereotype.Component;

import javax.mail.internet.MimeMessage;
import java.util.Arrays;
import java.util.List;

@Component
public class MailServerExtension {

    private GreenMail greenMail = new GreenMail(new ServerSetup(25, "localhost", ServerSetup.PROTOCOL_SMTP));

    public void start() {
        greenMail.start();
    }

    public void stop() {
        greenMail.stop();
    }

    public int getMessagesCount() {
        return greenMail.getReceivedMessages().length;
    }

    public List<MimeMessage> getMessages() {
        return Arrays.asList(greenMail.getReceivedMessages());
    }

    public MimeMessage getFirstMessage() {
        MimeMessage message = null;
        MimeMessage[] receivedMessages = greenMail.getReceivedMessages();
        if (receivedMessages.length > 0) {
            message = receivedMessages[0];
        }
        return message;
    }

}

src/test/java/ksbysample/common/test/rule/mail/MailServerResource.java は削除します。

次に JUnit 4 ベースで作成されている2つのファイルを以下の内容で変更します。

  • import org.junit.Testimport org.junit.jupiter.api.Test に変更します。
  • @RunWith(Enclosed) を削除します。
  • @RunWith(SpringRunner) を削除します。
  • クラスの中にテストクラスを記述して階層構造にしている場合には、中のクラスに @Nested アノテーションを付与します。
  • MailServerResource を MailServerExtension を使用するよう変更します。GreenMail のメールサーバの起動・停止は @BeforeEach@AfterEach アノテーションが付与されたメソッド内で行います。
    @Rule
    public MailServerResource mailServerResource = new MailServerResource()

   ↓  

    @Autowired
    private MailServerExtension mailServerExtension

    @BeforeEach
    void setup() {
        mailServerExtension.start()
        ..........
    }

    @AfterEach
    void cleanup() {
        mailServerExtension.stop()
    }
  • @Before@BeforeEach に変更します。

Spock ベースで実装している src/test/groovy/ksbysample/webapp/bootnpmgeb/web/inquiry/InquiryConfirmControllerTest.groovy も MailServerResource を使用していたので、以下の点を変更します。

  • @RunWith(Enclosed) を削除します。
  • MailServerResource を MailServerExtension を使用するよう変更します。GreenMail のメールサーバの起動・停止は setup メソッド、cleanup メソッド内で行います。
        @Rule
        MailServerResource mailServerResource = new MailServerResource()

   ↓  

        @Autowired
        private MailServerExtension mailServerExtension

        def setup() {
            mailServerExtension.start()
            ..........
        }

        def cleanup() {
            mailServerExtension.stop()
            ..........
        }
  • テストメソッド内の mailServerResource.mailServerExtension. に変更します。

clean タスク実行 → Rebuild Project 実行 → build タスクを実行すると "BUILD SUCCESSFUL" のメッセージが出力されましたが、成功したテストの件数が 147 → 131 になりました。まだテストメソッドが認識されないものが残っているようです。

f:id:ksby:20211010205913p:plain

続く。。。

長くなったので、次回へ続きます。以下の内容を行う予定です。

  • JUnit 4 の依存関係が残っているので削除する。
  • テスト件数が減っている原因を調査・解消する。
  • gebTest タスクが成功するか確認する。成功しない場合には、その原因を調査・解消する。
  • powermock で実装されたテストを powermock なしで動作するよう変更する。

履歴

2021/10/10
初版発行。