Spring Boot + npm + Geb で入力フォームを作ってテストする ( その98 )( Gradle を 6.4 → 6.9.1 へ、Spring Boot を 2.2.7 → 2.4.10 へ、Geb を 3.4 → 4.1 へバージョンアップする )
概要
記事一覧はこちらです。
Spring Boot + npm + Geb で入力フォームを作ってテストする ( その97 )( webpack を 4.43.0 → 5.56.0 へバージョンアップする ) の続きです。
- 今回の手順で確認できるのは以下の内容です。
- Gradle を 6.4 → 6.9.1 へバージョンアップします。
- Spring Boot を 2.2.7 → 2.4.10 へ、Geb を 3.4 → 4.1 へバージョンアップします。
参照したサイト・書籍
Spring Boot 2.4.2 and Thymeleaf 3.0.12 - access static methods
https://stackoverflow.com/questions/66048129/spring-boot-2-4-2-and-thymeleaf-3-0-12-access-static-methods/66053568Improve restricted expression evaluation mode
https://github.com/thymeleaf/thymeleaf/issues/809
目次
- バージョンアップの進め方を決める
- Gradle を 6.4 → 6.9.1 へバージョンアップする
- Spring Boot を 2.2.7 → 2.4.10 へ、Geb を 3.4 → 4.1 へバージョンアップする
手順
バージョンアップの進め方を決める
以下の手順で進めます。
- JDK 11 のまま、Gradle を 6系最新へ、Spring Boot を 2.2.7 → 2.4.10 へ、Geb を 3.4 → 4.1 へバージョンアップする(Groovy は 2系のまま)。
- JDK 11 のまま、Gradle を 7系最新へ、Spring Boot を 2.4.10 → 2.5.4 へ、Geb を 4.1 → 5.0 へバージョンアップする(Groovy を 3系へ)。
- JDK 17 へ、Spring Boot を 2.5.4 → 2.5.5 へバージョンアップする。
Gradle を 6.4 → 6.9.1 へバージョンアップする
build.gradle の wrapper タスクの記述を以下のように変更します。
wrapper {
gradleVersion = "6.9.1"
distributionType = Wrapper.DistributionType.ALL
}
gradleVersion = "6.4"
→gradleVersion = "6.9.1"
に変更します。
コマンドプロンプトから gradlew wrapper --gradle-version=6.9.1
コマンドを実行します。。。が、java.lang.IllegalArgumentException: Unsupported class file major version 61
のエラーが出力されてバージョンアップできませんでした。
いろいろ試してみたところ、どうも 6.4 の Gradle Deamon が起動している時に gradlew wrapper --gradle-version=6.9.1
コマンドを実行すると、このエラーが出るような気がします。PC を再起動して Gradle Daemon が何も実行されていない状態にしてみます(後で知りましたが gradlew --stop
コマンドを実行すれば Gradle Daemon は停止できました)。
PC 再起動後、コマンドプロンプトから gradlew wrapper --gradle-version=6.9.1
、gradlew --version
コマンドを実行すると今度は正常に終了しました。
gradle/wrapper/gradle-wrapper.properties は以下の内容になります。
distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists distributionUrl=https\://services.gradle.org/distributions/gradle-6.9.1-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists
Gradle Tool Window の左上にある「Refresh all Gradle projects」ボタンをクリックして更新します。
clean タスク実行 → Rebuild Project 実行 → build タスクを実行すると "BUILD SUCCESSFUL" のメッセージが出力されました。
Spring Boot を 2.2.7 → 2.4.10 へ、Geb を 3.4 → 4.1 へバージョンアップする
build.gradle の以下の点を変更します。
buildscript { ext { group "ksbysample" version "2.4.10" } repositories { mavenCentral() gradlePluginPortal() } dependencies { // for doma-codegen-plugin classpath "com.h2database:h2:1.4.200" } } plugins { id "java" id "eclipse" id "idea" id "org.springframework.boot" version "2.4.10" id "io.spring.dependency-management" version "1.0.11.RELEASE" id "groovy" 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.7.2") } } dependencies { def spockVersion = "1.3-groovy-2.5" def domaVersion = "2.49.0" def lombokVersion = "1.18.22" def errorproneVersion = "2.3.4" def powermockVersion = "2.0.7" 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") // dependency-management-plugin によりバージョン番号が自動で設定されないもの、あるいは最新バージョンを指定したいもの implementation("com.integralblue:log4jdbc-spring-boot-starter:2.0.0") implementation("org.flywaydb:flyway-core:5.2.4") implementation("com.h2database:h2:1.4.200") implementation("com.github.rozidan:modelmapper-spring-boot-starter:2.3.1") implementation("com.google.guava:guava:27.1-jre") implementation("org.apache.commons:commons-lang3:3.8.1") testImplementation("org.dbunit:dbunit:2.7.0") testImplementation("org.assertj:assertj-core:3.16.0") testImplementation("org.spockframework:spock-core:${spockVersion}") testImplementation("org.spockframework:spock-spring:${spockVersion}") testImplementation("org.jsoup:jsoup:1.13.1") testImplementation("com.icegreen:greenmail:1.5.13") // 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:3.4") { exclude group: "org.codehaus.groovy", module: "groovy-all" } testImplementation("org.seleniumhq.selenium:selenium-chrome-driver:${seleniumVersion}") testImplementation("org.seleniumhq.selenium:selenium-firefox-driver:${seleniumVersion}") testImplementation("org.seleniumhq.selenium:selenium-support:${seleniumVersion}") testImplementation("org.seleniumhq.selenium:selenium-api:${seleniumVersion}") testImplementation("org.seleniumhq.selenium:selenium-remote-driver:${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つが依存関係に追加される testCompile("org.junit.jupiter:junit-jupiter") testRuntime("org.junit.platform:junit-platform-launcher") } ..........
Spring Boot 2.4.10 へのバージョンアップとして以下の点を変更します。
- buildscript block の以下の点を変更します。
version "2.2.7-RELEASE"
→version "2.4.10"
- plugins block の以下の点を変更します。
id "org.springframework.boot" version "2.2.7.RELEASE"
→id "org.springframework.boot" version "2.4.10"
id "io.spring.dependency-management" version "1.0.9.RELEASE"
→id "io.spring.dependency-management" version "1.0.11.RELEASE"
- dependencies block の以下の点を変更します。
implementation("org.springframework.boot:spring-boot-starter-validation")
を追加します。
各種ライブラリのバージョンアップとして以下の点を変更します。
- plugins block の以下の点を変更します。
id "com.gorylenko.gradle-git-properties" version "2.2.2"
→id "com.gorylenko.gradle-git-properties" version "2.3.1"
id "org.seasar.doma.codegen" version "0.0.2"
→id "org.seasar.doma.codegen" version "1.4.1"
- dependencyManagement block の以下の点を変更します。
mavenBom org.springframework.boot.gradle.plugin.SpringBootPlugin.BOM_COORDINATES
→mavenBom(org.springframework.boot.gradle.plugin.SpringBootPlugin.BOM_COORDINATES) { bomProperty "groovy.version", "2.5.15" }
mavenBom("org.junit:junit-bom:5.6.2")
→mavenBom("org.junit:junit-bom:5.7.2")
- dependencies block の以下の点を変更します。
def lombokVersion = "1.18.12"
→def lombokVersion = "1.18.20"
def domaVersion = "2.34.0"
→def domaVersion = "2.49.0"
implementation("org.flywaydb:flyway-core:5.2.4")
→implementation("org.flywaydb:flyway-core:7.14.1")
implementation("com.google.guava:guava:27.1-jre")
→implementation("com.google.guava:guava:31.0.1-jre")
implementation("org.apache.commons:commons-lang3:3.8.1")
→implementation("org.apache.commons:commons-lang3:3.12.0")
testImplementation("org.dbunit:dbunit:2.7.0")
→testImplementation("org.dbunit:dbunit:2.7.2")
testImplementation("org.assertj:assertj-core:3.16.0")
→testImplementation("org.assertj:assertj-core:3.21.0")
testImplementation("org.jsoup:jsoup:1.13.1")
→testImplementation("org.jsoup:jsoup:1.14.3")
testImplementation("com.icegreen:greenmail:1.5.13")
→testImplementation("com.icegreen:greenmail:1.6.5")
testImplementation("org.gebish:geb-spock:3.4") {
→testImplementation("org.gebish:geb-spock:4.1") {
Gradle Tool Window の左上にある「Refresh all Gradle projects」ボタンをクリックして更新します。
clean タスク実行 → Rebuild Project 実行 → build タスクを実行すると compileJava タスクで警告が1件、testJUnit4AndSpock タスクでテストが大量に失敗しました。
compileJava タスクの警告は error-prone が build/generated の下のソースをチェックして出力していました。自動生成されたコードをチェックしないよう build.gradle に disableWarningsInGeneratedCode = true
の設定を追加します。
[compileJava, compileTestGroovy, compileTestJava]*.options*.encoding = "UTF-8" [compileJava, compileTestGroovy, compileTestJava]*.options*.compilerArgs = ["-Xlint:all,-options,-processing,-path"] tasks.withType(JavaCompile).configureEach { options.errorprone { disableWarningsInGeneratedCode = true } } tasks.named("compileTestJava").configure { options.errorprone.enabled = false }
testJUnit4AndSpock タスクでテストが大量に失敗している方は、失敗したテストを 1件だけ IntelliJ IDEA 上から実行してみると Caused by: org.flywaydb.core.api.FlywayException: Found non-empty schema(s) "PUBLIC" but no schema history table. Use baseline() or set baselineOnMigrate to true to initialize the schema history table.
というエラーメッセージが出力されていました。
Flyway が実行される時に PUBLIC スキーマが空でなく、spring.flyway.baseline-on-migrate=true
を設定すれば schema history table を初期化してくれるとのことですが、spring.flyway.baseline-on-migrate=true
を設定するのではなく PUBLIC テーブルが空でない原因を調べることにします。
application.properties を見ると以下の設定が記述されており、明らかにこれですね。。。 おそらくバージョンアップ前は Flywasy が先に実行されて Sprig Session のテーブル作成はその後に実行されていたのが、バージョンアップ後はその順序が逆になったものと思われます。
spring.session.store-type=jdbc spring.session.jdbc.initialize-schema=embedded spring.session.jdbc.schema=classpath:org/springframework/session/jdbc/schema-h2.sql spring.session.jdbc.table-name=SPRING_SESSION
spring.session.jdbc.initialize-schema=embedded
→ spring.session.jdbc.initialize-schema=never
に変更して Spring Session のテーブルを自動作成しないように変更してから、
spring.session.store-type=jdbc spring.session.jdbc.initialize-schema=never spring.session.jdbc.table-name=SPRING_SESSION
失敗していたテスト(このテストは Spring Session の機能を利用していない)を実行してみると、成功するようになりました。
spring.session.jdbc.initialize-schema=never
の設定はそのままにして、Flyway で Spring Session のテーブルを作成するようにします。
src/main/resources/db/migration の下に V1.2__create_spring_session_schema.sql を新規作成した後、org/springframework/session/jdbc/schema-h2.sql の内容をコピーします。
-- copy from org/springframework/session/jdbc/schema-h2.sql CREATE TABLE SPRING_SESSION ( PRIMARY_ID CHAR(36) NOT NULL, SESSION_ID CHAR(36) NOT NULL, CREATION_TIME BIGINT NOT NULL, LAST_ACCESS_TIME BIGINT NOT NULL, MAX_INACTIVE_INTERVAL INT NOT NULL, EXPIRY_TIME BIGINT NOT NULL, PRINCIPAL_NAME VARCHAR(100), CONSTRAINT SPRING_SESSION_PK PRIMARY KEY (PRIMARY_ID) ); CREATE UNIQUE INDEX SPRING_SESSION_IX1 ON SPRING_SESSION (SESSION_ID); CREATE INDEX SPRING_SESSION_IX2 ON SPRING_SESSION (EXPIRY_TIME); CREATE INDEX SPRING_SESSION_IX3 ON SPRING_SESSION (PRINCIPAL_NAME); CREATE TABLE SPRING_SESSION_ATTRIBUTES ( SESSION_PRIMARY_ID CHAR(36) NOT NULL, ATTRIBUTE_NAME VARCHAR(200) NOT NULL, ATTRIBUTE_BYTES LONGVARBINARY NOT NULL, CONSTRAINT SPRING_SESSION_ATTRIBUTES_PK PRIMARY KEY (SESSION_PRIMARY_ID, ATTRIBUTE_NAME), CONSTRAINT SPRING_SESSION_ATTRIBUTES_FK FOREIGN KEY (SESSION_PRIMARY_ID) REFERENCES SPRING_SESSION (PRIMARY_ID) ON DELETE CASCADE );
再度 clean タスク実行 → Rebuild Project 実行 → build タスクを実行すると、テストがまだ 2件失敗します。
原因を調査すると、改行コードを <br/>
に変換している th:utext="*{inquiry} ? ${#strings.replace(#strings.escapeXml(confirmForm.inquiry), T(java.lang.System).getProperty('line.separator'), '<br />')} : ''"
のところで org.attoparser.ParseException: Instantiation of new objects and access to static classes is forbidden in this context
というエラーメッセージが出力されていました。
<tr> <th nowrap>お問い合わせの内容</th> <td id="inquiry" th:utext="*{inquiry} ? ${#strings.replace(#strings.escapeXml(confirmForm.inquiry), T(java.lang.System).getProperty('line.separator'), '<br />')} : ''"> ここに、<br/> 入力されたお問い合わせの内容が表示されます。 </td> </tr>
Web で調べると Spring Boot 2.4.2 and Thymeleaf 3.0.12 - access static methods を見つけました。Thymeleaf 3.0.12 からセキュリティ強化のために T(identifier) in SpringEL
の形式では呼び出せなくなったとのこと。
System.getProperty を呼び出す Bean を作成して、Thymeleaf テンプレートから呼び出すように変更します。
まず src/main/java/ksbysample/webapp/bootnpmgeb/helper の下に thymeleaf パッケージを作成し、その下に SystemPropertiesHelper.java を新規作成して以下の内容を記述します。
package ksbysample.webapp.bootnpmgeb.helper.thymeleaf; import org.springframework.boot.system.SystemProperties; import org.springframework.stereotype.Component; /** * System Property 値取得用 Helper クラス */ @Component("sph") public class SystemPropertiesHelper { /** * System#getProperty を呼び出して指定された system property の値を取得する * * @param properties 値を取得する system property 名 * @return 取得した system property の値 */ public String getProperty(String... properties) { return SystemProperties.get(properties); } }
src/main/resources/templates/web/inquiry/confirm.html を以下のように変更します。
<tr> <th nowrap>お問い合わせの内容</th> <td id="inquiry" th:utext="*{inquiry} ? ${#strings.replace(#strings.escapeXml(confirmForm.inquiry), @sph.getProperty('line.separator'), '<br />')} : ''"> ここに、<br/> 入力されたお問い合わせの内容が表示されます。 </td> </tr>
T(java.lang.System).getProperty('line.separator')
→@sph.getProperty('line.separator')
に変更します。
再度 clean タスク実行 → Rebuild Project 実行 → build タスクを実行すると、今度は "BUILD SUCCESSFUL" のメッセージが出力されました。
gebTest タスクも "BUILD SUCCESSFUL" のメッセージが表示されました。Geb のバージョンアップは特に問題は発生しないようです。
履歴
2021/10/10
初版発行。