Spring Boot + npm + Geb で入力フォームを作ってテストする ( その39 )( Spring Boot を 1.5.7 → 1.5.9 へバージョンアップする )
概要
記事一覧はこちらです。
Spring Boot + npm + Geb で入力フォームを作ってテストする ( その38 )( IntelliJ IDEA から Jest のテストを実行する ) の続きです。
- 今回の手順で確認できるのは以下の内容です。
- Spring IO Platform の Brussels-SR6 がリリースされていることに気づきました。Spring Boot のバージョンを 1.5.7 → 1.5.9 へバージョンアップします。
- 他にもバージョンアップしているライブラリを更新します。
参照したサイト・書籍
目次
手順
Spring Boot を 1.5.7 → 1.5.9 へバージョンアップする(他のライブラリもバージョンアップする)
build.gradle の以下の点を変更します。
group 'ksbysample' version '1.0.0-RELEASE' buildscript { ext { springBootVersion = '1.5.9.RELEASE' } repositories { mavenCentral() maven { url "https://plugins.gradle.org/m2/" } } dependencies { classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}") // for Error Prone ( http://errorprone.info/ ) classpath("net.ltgt.gradle:gradle-errorprone-plugin:0.0.13") } } apply plugin: 'java' apply plugin: 'eclipse' apply plugin: 'idea' apply plugin: 'org.springframework.boot' apply plugin: 'groovy' apply plugin: 'net.ltgt.errorprone' apply plugin: 'checkstyle' apply plugin: 'findbugs' apply plugin: 'pmd' sourceCompatibility = 1.8 targetCompatibility = 1.8 task wrapper(type: Wrapper) { gradleVersion = '3.5' } [compileJava, compileTestGroovy, compileTestJava]*.options*.compilerArgs = ['-Xlint:all,-options,-processing,-path'] compileJava.options.compilerArgs += [ '-Xep:RemoveUnusedImports:WARN' , '-Xep:NestedInstanceOfConditions:OFF' , '-Xep:InstanceOfAndCastMatchWrongType:OFF' ] // for Doma 2 // JavaクラスとSQLファイルの出力先ディレクトリを同じにする processResources.destinationDir = compileJava.destinationDir // コンパイルより前にSQLファイルを出力先ディレクトリにコピーするために依存関係を逆転する compileJava.dependsOn processResources idea { module { inheritOutputDirs = false outputDir = file("$buildDir/classes/main/") } } configurations { // for Doma 2 domaGenRuntime } checkstyle { configFile = file("${rootProject.projectDir}/config/checkstyle/google_checks.xml") toolVersion = '8.5' sourceSets = [project.sourceSets.main] } findbugs { toolVersion = '3.0.1' sourceSets = [project.sourceSets.main] ignoreFailures = true effort = "max" excludeFilter = file("${rootProject.projectDir}/config/findbugs/findbugs-exclude.xml") } tasks.withType(FindBugs) { // Gradle 3.3以降 + FindBugs Gradle Plugin を組み合わせると、"The following errors occurred during analysis:" // の後に "Cannot open codebase filesystem:..." というメッセージが大量に出力されるので、以下の doFirst { ... } // のコードを入れることで出力されないようにする doFirst { def fc = classes if (fc == null) { return } fc.exclude '**/*.properties' fc.exclude '**/*.sql' fc.exclude '**/*.xml' fc.exclude '**/META-INF/**' fc.exclude '**/static/**' fc.exclude '**/templates/**' classes = files(fc.files) } reports { xml.enabled = false html.enabled = true } } pmd { toolVersion = "5.8.1" sourceSets = [project.sourceSets.main] ignoreFailures = true consoleOutput = true ruleSetFiles = rootProject.files("/config/pmd/pmd-project-rulesets.xml") ruleSets = [] } repositories { mavenCentral() } dependencyManagement { imports { // BOM は https://repo.spring.io/release/io/spring/platform/platform-bom/Brussels-SR6/ // の下を見ること mavenBom("io.spring.platform:platform-bom:Brussels-SR6") { bomProperty 'guava.version', '22.0' bomProperty 'thymeleaf.version', '3.0.9.RELEASE' bomProperty 'thymeleaf-extras-springsecurity4.version', '3.0.2.RELEASE' bomProperty 'thymeleaf-layout-dialect.version', '2.2.2' bomProperty 'thymeleaf-extras-data-attribute.version', '2.0.1' bomProperty 'thymeleaf-extras-java8time.version', '3.0.1.RELEASE' } } } bootRepackage { mainClass = 'ksbysample.webapp.lending.Application' } dependencies { def spockVersion = "1.1-groovy-2.4" def domaVersion = "2.16.1" def lombokVersion = "1.16.18" def errorproneVersion = "2.1.3" def powermockVersion = "1.7.3" def seleniumVersion = "3.8.1" // dependency-management-plugin によりバージョン番号が自動で設定されるもの // Appendix A. Dependency versions ( http://docs.spring.io/platform/docs/current/reference/htmlsingle/#appendix-dependency-versions ) 参照 compile("org.springframework.boot:spring-boot-starter-web") compile("org.springframework.boot:spring-boot-starter-thymeleaf") { exclude group: "org.codehaus.groovy", module: "groovy" } compile("org.springframework.boot:spring-boot-starter-data-jpa") compile("org.springframework.boot:spring-boot-starter-freemarker") compile("org.springframework.boot:spring-boot-starter-mail") compile("org.springframework.boot:spring-boot-starter-security") compile("org.springframework.boot:spring-boot-devtools") compile("org.springframework.session:spring-session") compile("com.google.guava:guava") compile("org.apache.commons:commons-lang3") compile("org.codehaus.janino:janino") testCompile("org.springframework.boot:spring-boot-starter-test") testCompile("org.springframework.security:spring-security-test") testCompile("org.yaml:snakeyaml") testCompile("org.mockito:mockito-core") // dependency-management-plugin によりバージョン番号が自動で設定されないもの、あるいは最新バージョンを指定したいもの compile("com.integralblue:log4jdbc-spring-boot-starter:1.0.2") compile("org.flywaydb:flyway-core:4.2.0") compile("com.h2database:h2:1.4.192") compile("com.github.rozidan:modelmapper-spring-boot-starter:1.0.0") testCompile("org.dbunit:dbunit:2.5.4") testCompile("com.icegreen:greenmail:1.5.5") testCompile("org.assertj:assertj-core:3.8.0") testCompile("org.spockframework:spock-core:${spockVersion}") testCompile("org.spockframework:spock-spring:${spockVersion}") testCompile("com.google.code.findbugs:jsr305:3.0.2") testCompile("org.jsoup:jsoup:1.11.2") // for lombok compileOnly("org.projectlombok:lombok:${lombokVersion}") testCompileOnly("org.projectlombok:lombok:${lombokVersion}") // for Doma compile("org.seasar.doma:doma:${domaVersion}") domaGenRuntime("org.seasar.doma:doma-gen:${domaVersion}") domaGenRuntime("com.h2database:h2:1.4.192") // for Error Prone ( http://errorprone.info/ ) errorprone("com.google.errorprone:error_prone_core:${errorproneVersion}") compileOnly("com.google.errorprone:error_prone_annotations:${errorproneVersion}") // PowerMock testCompile("org.powermock:powermock-module-junit4:${powermockVersion}") testCompile("org.powermock:powermock-api-mockito:${powermockVersion}") // for Geb + Spock testCompile("org.gebish:geb-spock:2.0") testCompile("org.seleniumhq.selenium:selenium-chrome-driver:${seleniumVersion}") testCompile("org.seleniumhq.selenium:selenium-firefox-driver:${seleniumVersion}") testCompile("org.seleniumhq.selenium:selenium-support:${seleniumVersion}") testCompile("org.seleniumhq.selenium:selenium-api:${seleniumVersion}") testCompile("org.seleniumhq.selenium:selenium-remote-driver:${seleniumVersion}") } ..........
buildscript
の以下の点を変更します。springBootVersion = '1.5.7.RELEASE'
→springBootVersion = '1.5.9.RELEASE'
に変更します。
checkstyle
の以下の点を変更します。toolVersion = '8.3'
→toolVersion = '8.5'
に変更します。
dependencyManagement
の以下の点を変更します。mavenBom("io.spring.platform:platform-bom:Brussels-SR5")
→mavenBom("io.spring.platform:platform-bom:Brussels-SR6")
に変更します。- thymeleaf.version を
3.0.8.RELEASE
→3.0.9.RELEASE
に変更します。
dependencies
の以下の点を変更します。def domaVersion = "2.16.1"
→def domaVersion = "2.19.0"
に変更します。def errorproneVersion = "2.1.1"
→def errorproneVersion = "2.1.3"
に変更します。def seleniumVersion = "3.6.0"
→def seleniumVersion = "3.8.1"
に変更します。testCompile("com.icegreen:greenmail:1.5.5")
→testCompile("com.icegreen:greenmail:1.5.6")
に変更します。testCompile("org.jsoup:jsoup:1.10.3")
→testCompile("org.jsoup:jsoup:1.11.2")
に変更します。
変更後、Gradle Tool Window の左上にある「Refresh all Gradle projects」ボタンをクリックして更新します。
clean タスク実行 → Rebuild Project 実行 → build タスクを実行してみますが、error-prone で エラー: An unhandled exception was thrown by the Error Prone static analysis plugin.
というエラーが出ました。
コマンドプロンプトから gradlew --stacktrace build
コマンドを実行してみると、今回は com.google.errorprone.bugpatterns.ParameterName.checkArguments
で引っかかっていました。
build.gradle を修正し、-Xep:ParameterName:OFF
オプションを追加します。
[compileJava, compileTestGroovy, compileTestJava]*.options*.compilerArgs = ['-Xlint:all,-options,-processing,-path'] compileJava.options.compilerArgs += [ '-Xep:RemoveUnusedImports:WARN' , '-Xep:NestedInstanceOfConditions:OFF' , '-Xep:InstanceOfAndCastMatchWrongType:OFF' , '-Xep:ParameterName:OFF' ]
再び clean タスク実行 → Rebuild Project 実行 → build タスクを実行すると、今度は "BUILD SUCCESSFUL" が出力されました。
Project Tool Window で src/test/groovy/ksbysample を選択した後、コンテキストメニューを表示して「Run 'Tests in ksbysample
' with Coverage」を選択し、テストが全て成功することも確認しておきます。
Tomcat を起動してから gebTest タスクを実行し Geb のテストも成功することを確認しようと思ったのですが、12 tests completed, 10 failed
と一部のテストが失敗しました。
firefoxTest タスクと chromeTest タスクをそれぞれ個別に動かしてみましたが、どうも入力画面1→入力画面2へうまく遷移できていないようです。
いろいろ調べた結果、テストが失敗するようになった原因は Spring Boot + npm + Geb で入力フォームを作ってテストする ( その35 )( Geb でテストを作成する2 ) で src/test/groovy/geb/module/FormModule.groovy に追加した $(it.key) << Keys.TAB
でした。これを以下のようにコメントアウトしてから、
void setValueList(valueList) { valueList.each { $(it.key).value(it.value) // $(it.key) << Keys.TAB } }
firefoxTest タスクを実行するとテストは全て成功します。
どうも $(it.key) << Keys.TAB
を実行した後だと、単に $("#inquiryInput01Form").sex = "1"
と書いただけでは値がセットできないようです。$(it.key).value(it.value)
のように Navigator#value メソッドを使用すればセットできたので、これを使用する方法に変更します。
まずは src/test/groovy/geb/module/FormModule.groovy を以下のように変更します。
package geb.module import geb.Module import org.openqa.selenium.Keys import org.openqa.selenium.WebElement /** * フォーム共通の content や、テストに使用するメソッドを定義するクラス */ class FormModule extends Module { static content = { btnBack { $(".js-btn-back") } btnNext { $(".js-btn-next") } } /** * Form の入力項目に値をセットする * @param selector セットする要素のセレクタ * @param value セットする値 */ void setValue(selector, value) { $(selector).value(value) $(selector) << Keys.TAB } /** * Form の入力項目に値を一括セットする * valueList は以下の形式の Map である * <pre>{@code * static initialValueList = [ * "#lastname" : "", * "#firstname" : "", * "#lastkana" : "", * "#firstkana" : "", * "input[name='sex']": null, * "#age" : "", * "#job" : "" * ] *}</pre> * * @param valueList セットするセレクタと値を記述した Map */ void setValueList(valueList) { valueList.each { setValue(it.key, it.value) } } .......... }
setValue
メソッドを追加します。setValueList
メソッド内の値をセットする処理をsetValue
メソッドを呼び出すように変更します。
次に src/test/groovy/geb/gebspec/inquiry/InquiryTestSpec.groovy 内で入力項目に値をセットする処理を form.setValue メソッドを使用するように変更します。
package geb.gebspec.inquiry import geb.page.inquiry.InquiryInput01Page import geb.page.inquiry.InquiryInput02Page import geb.spock.GebSpec import org.openqa.selenium.Keys import org.openqa.selenium.WebElement import spock.lang.Unroll class InquiryTestSpec extends GebSpec { def "入力画面1の画面初期表示時に想定している値がセットされている"() { setup: "入力画面1を表示する" to InquiryInput01Page expect: "初期値が表示されている" form.assertValueList(initialValueList) } def "入力画面1の各入力項目に最大文字数を入力できる"() { setup: "入力画面1を表示し入力項目に最大文字数の文字を入力する" to InquiryInput01Page form.setValueList(maxLengthValueList) expect: "入力した最大文字数の文字が入力されている" form.assertValueList(maxLengthValueList) } def "入力画面1に入力後、入力画面2へ遷移→入力画面1へ戻ると入力した値が表示される"() { given: "入力画面1を表示する" to InquiryInput01Page when: "最大文字数の文字を入力して次へボタンをクリックする" form.setValueList(maxLengthValueList) form.setValue("input[name='sex']", "1") form.btnNext.click(InquiryInput02Page) then: "入力画面2へ遷移し初期値が表示されている" form.assertValueList(initialValueList) and: "戻るボタンをクリックする" form.btnBack.click(InquiryInput01Page) then: "入力した最大文字数の文字が入力されている" form.assertValueList(maxLengthValueList) $("#inquiryInput01Form").sex == "1" } def "入力画面2の各入力項目に最大文字数を入力できる"() { setup: "入力画面1を表示して最大文字数の文字を入力してから次へボタンをクリックする" to InquiryInput01Page form.setValueList(maxLengthValueList) form.setValue("input[name='sex']", "1") form.btnNext.click(InquiryInput02Page) and: "入力画面2で最大文字数の文字を入力する" form.setValueList(maxLengthValueList) expect: "入力した最大文字数の文字が入力されている" form.assertValueList(maxLengthValueList) } def "入力画面2で郵便番号を入力してautocompleteで表示された住所を選択する"() { given: "入力画面1から入力画面2へ遷移する" to InquiryInput01Page form.setValueList(maxLengthValueList) form.setValue("input[name='sex']", "1") form.btnNext.click(InquiryInput02Page) when: "郵便番号を入力する" $("#inquiryInput02Form").zipcode1 = "100" $("#inquiryInput02Form").zipcode2 = "0005" << Keys.TAB and: "autocomplete のドロップダウンメニューが表示されたら最初の選択肢を選択する" waitFor(5) { $(".ui-autocomplete .ui-menu-item") } List<WebElement> elementList = $(".ui-autocomplete .ui-menu-item > div").allElements() elementList.first().click() then: "住所にクリックした選択肢がセットされている" $("#inquiryInput02Form").address == "東京都千代田区丸の内" } @Unroll def "入力画面2の電話番号とメールアドレスの組み合わせテスト(#tel1,#tel2,#tel3,#email)"() { given: "入力画面1から入力画面2へ遷移する" to InquiryInput01Page form.setValueList(maxLengthValueList) form.setValue("input[name='sex']", "1") form.btnNext.click(InquiryInput02Page) when: "電話番号と郵便番号を入力する" form.setValue("#tel1", tel1) form.setValue("#tel2", tel2) form.setValue("#tel3", tel3) form.setValue("#email", email) then: "エラーメッセージの表示状況をチェックする" $("#form-group-tel .js-errmsg").text() == telErrMsg $("#form-group-email .js-errmsg").text() == emailErrMsg where: tel1 | tel2 | tel3 | email || telErrMsg | emailErrMsg "03" | "1234" | "5678" | "tanaka@sample.co.jp" || "" | "" "03" | "1234" | "5678" | "" || "" | "" "" | "" | "" | "tanaka@sample.co.jp" || "" | "" "" | "" | "" | "" || "電話番号とメールアドレスのいずれか一方を入力してください" | "電話番号とメールアドレスのいずれか一方を入力してください" "3" | "1234" | "5678" | "" || "市外局番の先頭には 0 の数字を入力してください" | "" "03" | "123" | "5678" | "" || "市外局番+市内局番の組み合わせが数字6桁になるように入力してください" | "" "03" | "1234" | "567" | "" || "加入者番号には4桁の数字を入力してください" | "" } }
再び gebTest タスクを実行すると、テストが全て成功するようになりました。
履歴
2017/12/09
初版発行。