Spring Boot + npm + Geb で入力フォームを作ってテストする ( その35 )( Geb でテストを作成する2 )
概要
記事一覧はこちらです。
Spring Boot + npm + Geb で入力フォームを作ってテストする ( その34 )( Geb でテストを作成する ) の続きです。
参照したサイト・書籍
目次
手順
HtmlUnit でテストが失敗した原因を取り除いてみる
前回 HtmlUnit でテストを実行したら java.lang.NoSuchMethodError: com.gargoylesoftware.htmlunit.html.DomElement.getScriptableObject()Ljava/lang/Object;
のエラーが出て失敗しました。
ライブラリの依存関係に問題がある気がします。gradlew dependencies
コマンドを実行してみたところ、以下の内容が出力されました。
+--- org.seleniumhq.selenium:htmlunit-driver:2.27 | +--- org.seleniumhq.selenium:selenium-api:3.4.0 -> 3.6.0 | +--- org.seleniumhq.selenium:selenium-support:3.4.0 -> 3.6.0 | | +--- org.seleniumhq.selenium:selenium-api:3.6.0 | | +--- org.seleniumhq.selenium:selenium-remote-driver:3.6.0 (*) | | +--- net.bytebuddy:byte-buddy:1.7.5 | | +--- org.apache.commons:commons-exec:1.3 | | +--- commons-codec:commons-codec:1.10 | | +--- commons-logging:commons-logging:1.2 | | +--- com.google.code.gson:gson:2.8.0 -> 2.8.1 | | +--- com.google.guava:guava:23.0 -> 22.0 (*) | | +--- org.apache.httpcomponents:httpclient:4.5.3 (*) | | +--- org.apache.httpcomponents:httpcore:4.4.6 | | +--- net.java.dev.jna:jna:4.1.0 -> 4.2.2 | | \--- net.java.dev.jna:jna-platform:4.1.0 (*) | \--- net.sourceforge.htmlunit:htmlunit:2.27 -> 2.21 | +--- xalan:xalan:2.7.2 | | \--- xalan:serializer:2.7.2 | +--- org.apache.commons:commons-lang3:3.4 -> 3.5 | +--- org.apache.httpcomponents:httpclient:4.5.2 -> 4.5.3 (*) | +--- org.apache.httpcomponents:httpmime:4.5.2 -> 4.5.3 | | \--- org.apache.httpcomponents:httpclient:4.5.3 (*) | +--- commons-codec:commons-codec:1.10 | +--- net.sourceforge.htmlunit:htmlunit-core-js:2.17 | +--- net.sourceforge.htmlunit:neko-htmlunit:2.21 | | \--- xerces:xercesImpl:2.11.0 | | \--- xml-apis:xml-apis:1.4.01 | +--- net.sourceforge.cssparser:cssparser:0.9.18 | | \--- org.w3c.css:sac:1.3 | +--- commons-io:commons-io:2.4 -> 2.5 | +--- commons-logging:commons-logging:1.2 | \--- org.eclipse.jetty.websocket:websocket-client:9.2.15.v20160210 -> 9.4.6.v20170531 | +--- org.eclipse.jetty:jetty-util:9.4.6.v20170531 | +--- org.eclipse.jetty:jetty-io:9.4.6.v20170531 | | \--- org.eclipse.jetty:jetty-util:9.4.6.v20170531 | +--- org.eclipse.jetty:jetty-client:9.4.6.v20170531 | | +--- org.eclipse.jetty:jetty-http:9.4.6.v20170531 | | | +--- org.eclipse.jetty:jetty-util:9.4.6.v20170531 | | | \--- org.eclipse.jetty:jetty-io:9.4.6.v20170531 (*) | | \--- org.eclipse.jetty:jetty-io:9.4.6.v20170531 (*) | \--- org.eclipse.jetty.websocket:websocket-common:9.4.6.v20170531 | +--- org.eclipse.jetty.websocket:websocket-api:9.4.6.v20170531 | +--- org.eclipse.jetty:jetty-util:9.4.6.v20170531 | \--- org.eclipse.jetty:jetty-io:9.4.6.v20170531 (*) +--- org.seleniumhq.selenium:selenium-support:3.6.0 (*) +--- org.seleniumhq.selenium:selenium-api:3.6.0 \--- org.seleniumhq.selenium:selenium-remote-driver:3.6.0 (*)
net.sourceforge.htmlunit:htmlunit:2.27 -> 2.21
とバージョンダウンされているモジュールがありました。net.sourceforge.htmlunit:htmlunit
の 2.27 が使用されるよう build.gradle を以下のように変更します。
dependencies { .......... // for Geb + Spock .......... testCompile("org.seleniumhq.selenium:htmlunit-driver:2.27") testCompile("net.sourceforge.htmlunit:htmlunit:2.27") .......... }
testCompile("net.sourceforge.htmlunit:htmlunit:2.27")
を追加します。
変更後、Gradle Tool Window の左上にある「Refresh all Gradle projects」ボタンをクリックして更新します。
再び gradlew dependencies
コマンドを実行すると、今度は net.sourceforge.htmlunit:htmlunit:2.27
と出力されました。
+--- org.seleniumhq.selenium:htmlunit-driver:2.27 | +--- org.seleniumhq.selenium:selenium-api:3.4.0 -> 3.6.0 | +--- org.seleniumhq.selenium:selenium-support:3.4.0 -> 3.6.0 | | +--- org.seleniumhq.selenium:selenium-api:3.6.0 | | +--- org.seleniumhq.selenium:selenium-remote-driver:3.6.0 (*) | | +--- net.bytebuddy:byte-buddy:1.7.5 | | +--- org.apache.commons:commons-exec:1.3 | | +--- commons-codec:commons-codec:1.10 | | +--- commons-logging:commons-logging:1.2 | | +--- com.google.code.gson:gson:2.8.0 -> 2.8.1 | | +--- com.google.guava:guava:23.0 -> 22.0 (*) | | +--- org.apache.httpcomponents:httpclient:4.5.3 (*) | | +--- org.apache.httpcomponents:httpcore:4.4.6 | | +--- net.java.dev.jna:jna:4.1.0 -> 4.2.2 | | \--- net.java.dev.jna:jna-platform:4.1.0 (*) | \--- net.sourceforge.htmlunit:htmlunit:2.27 | +--- xalan:xalan:2.7.2 | | \--- xalan:serializer:2.7.2 | +--- org.apache.commons:commons-lang3:3.5 | +--- org.apache.httpcomponents:httpmime:4.5.3 | | \--- org.apache.httpcomponents:httpclient:4.5.3 (*) | +--- net.sourceforge.htmlunit:htmlunit-core-js:2.27 | +--- net.sourceforge.htmlunit:neko-htmlunit:2.27 | | \--- xerces:xercesImpl:2.11.0 | | \--- xml-apis:xml-apis:1.4.01 | +--- net.sourceforge.cssparser:cssparser:0.9.23 | | \--- org.w3c.css:sac:1.3 | +--- commons-io:commons-io:2.5 | +--- commons-logging:commons-logging:1.2 | \--- org.eclipse.jetty.websocket:websocket-client:9.4.5.v20170502 -> 9.4.6.v20170531 | +--- org.eclipse.jetty:jetty-util:9.4.6.v20170531 | +--- org.eclipse.jetty:jetty-io:9.4.6.v20170531 | | \--- org.eclipse.jetty:jetty-util:9.4.6.v20170531 | +--- org.eclipse.jetty:jetty-client:9.4.6.v20170531 | | +--- org.eclipse.jetty:jetty-http:9.4.6.v20170531 | | | +--- org.eclipse.jetty:jetty-util:9.4.6.v20170531 | | | \--- org.eclipse.jetty:jetty-io:9.4.6.v20170531 (*) | | \--- org.eclipse.jetty:jetty-io:9.4.6.v20170531 (*) | \--- org.eclipse.jetty.websocket:websocket-common:9.4.6.v20170531 | +--- org.eclipse.jetty.websocket:websocket-api:9.4.6.v20170531 | +--- org.eclipse.jetty:jetty-util:9.4.6.v20170531 | \--- org.eclipse.jetty:jetty-io:9.4.6.v20170531 (*) +--- net.sourceforge.htmlunit:htmlunit:2.27 (*) +--- org.seleniumhq.selenium:selenium-support:3.6.0 (*) +--- org.seleniumhq.selenium:selenium-api:3.6.0 \--- org.seleniumhq.selenium:selenium-remote-driver:3.6.0 (*)
再度 HtmlUnit でテストを実行してみると、今度は成功するテストが出てきました。ただし、まだ一部のテストが失敗しています。失敗したテストを見てみると、
こちらは autocomplete のテストで失敗しており、5秒経過しても指定した selector が見つからないようです。
こちらはエラーメッセージが表示されていないようです。
いろいろ試してみると、後者のエラーメッセージが出ない方はテストを以下のように変更すると解消されました。
when: "電話番号と郵便番号を入力する" $("#inquiryInput02Form").tel1 = tel1 << Keys.TAB $("#inquiryInput02Form").tel2 = tel2 << Keys.TAB $("#inquiryInput02Form").tel3 = tel3 << Keys.TAB $("#inquiryInput02Form").email = email << Keys.TAB
↓↓↓
when: "電話番号と郵便番号を入力する" $("#inquiryInput02Form").tel1 = tel1 $("#tel2") << "" $("#inquiryInput02Form").tel2 = tel2 $("#tel3") << "" $("#inquiryInput02Form").tel3 = tel3 $("#email") << "" $("#inquiryInput02Form").email = email $("#tel1") << ""
テストを実行すると autocomplete 以外のテストが成功します。
HtmlUnit だと Keys.TAB を送信しても次の入力項目にフォーカスが移動せず、フォーカスを移動したい入力項目に << ""
を送信しないといけないようです。
ということは autocomplete のテストを以下のように修正すると、
when: "郵便番号を入力する" $("#inquiryInput02Form").zipcode1 = "100" $("#inquiryInput02Form").zipcode2 = "0005" << Keys.TAB
↓↓↓
when: "郵便番号を入力する" $("#inquiryInput02Form").zipcode1 = "100" $("#inquiryInput02Form").zipcode2 = "0005" $("#address") << ""
今度は全てのテストが成功しました。
HtmlUnit は Geb のテスト対象ブラウザから外す
$("#inquiryInput02Form").tel1 = tel1 << Keys.TAB
のように記述して TAB キーを押したら次の入力項目にフォーカスが移動できた方が個人的には分かりやすく、かつテストが書きやすいのと、Firefox や Chrome の headless が十分使いやすいので、HtmlUnit は Geb のテスト対象ブラウザから外すことにします。
build.gradle を以下のように変更します。
dependencies { .......... // 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}") } .......... // for Geb + Spock Integration Test def drivers = ["chrome", "firefox"] drivers.each { driver -> task "${driver}Test"(type: Test) { // 前回実行時以降に何も更新されていなくても必ず実行する outputs.upToDateWhen { false } systemProperty "geb.env", driver exclude "ksbysample/**" } } task gebTest { dependsOn drivers.collect { tasks["${it}Test"] } enabled = false }
- 以下の2行を削除します。
testCompile("org.seleniumhq.selenium:htmlunit-driver:2.27")
testCompile("net.sourceforge.htmlunit:htmlunit:2.27")
def drivers = ["chrome", "firefox", "htmlunit"]
→def drivers = ["chrome", "firefox"]
に変更します。
変更後、Gradle Tool Window の左上にある「Refresh all Gradle projects」ボタンをクリックして更新します。
src/test/resources/GebConfig.groovy を以下のように変更します。
environments { chrome { driver = { ChromeOptions chromeOptions = new ChromeOptions() chromeOptions.setHeadless(true) new ChromeDriver(chromeOptions) } } firefox { driver = { FirefoxOptions firefoxOptions = new FirefoxOptions() firefoxOptions.setHeadless(true) new FirefoxDriver(firefoxOptions) } } }
- environments から
htmlunit { new HtmlUnitDriver(true) }
を削除します。
gebTest タスクを実行して "BUILD SUCCESSFUL" が表示されることを確認します。
Geb でいろいろ試してみる
画面の外にボタンがあるとクリックできない?
Geb について書かれた記事を見ていると、ボタンがブラウザの Window 内に表示されていない場合クリックできないと書かれたものを何回か見かけので、確認してみます。
まずは <br>
タグを大量に入れて、「次へ」ボタンを画面の外に追い出します。
src/test/groovy/geb/gebspec/inquiry/InquiryTestSpec.groovy に以下のテストを記述します。
def "次へボタンが画面の外にあるとクリックできない?" () { setup: "入力画面1を表示してデータを入力する" to InquiryInput01Page form.setValueList(maxLengthValueList) $("#inquiryInput01Form").sex = "1" // 念のため、フォーカスを「お名前(漢字)」へ移動する $("#lastname") << "" expect: "次へボタンをクリックする" form.btnNext.click(InquiryInput02Page) }
テストを実行すると問題なく成功しますね。。。
src/test/resources/GebConfig.groovy を以下のように変更して headless モードを解除し、
driver = { FirefoxOptions firefoxOptions = new FirefoxOptions() // firefoxOptions.setHeadless(true) new FirefoxDriver(firefoxOptions) }
src/test/groovy/geb/gebspec/inquiry/InquiryTestSpec.groovy に Thread.sleep(3000)
を追加して、
def "次へボタンが画面の外にあるとクリックできない?" () { setup: "入力画面1を表示してデータを入力する" to InquiryInput01Page form.setValueList(maxLengthValueList) $("#inquiryInput01Form").sex = "1" // 念のため、フォーカスを「お名前(漢字)」へ移動する $("#lastname") << "" Thread.sleep(3000) expect: "次へボタンをクリックする" form.btnNext.click(InquiryInput02Page) }
テストを実行してブラウザの動きを見てみたところ、form.btnNext.click(InquiryInput02Page)
の処理を実行する時に「次へ」ボタンへフォーカスが移動して画面の見える位置に移動していました。Firefox Quantum だと問題なく動作するようです。
src/test/resources/GebConfig.groovy を以下のように変更してブラウザを Chrome に変更しても、
driver = { // FirefoxOptions firefoxOptions = new FirefoxOptions() // firefoxOptions.setHeadless(true) // new FirefoxDriver(firefoxOptions) ChromeOptions chromeOptions = new ChromeOptions() chromeOptions.setHeadless(true) new ChromeDriver(chromeOptions) }
テストは成功しました。Chrome も問題ないようです。
gradle から gebTest タスクを実行しても成功しました。
画面の外にボタンがあっても問題なくクリックできるようです。変更したソースは元に戻します。
maxlength を超える文字列は入力できない
Geb で値をセットした時に maxlength を超える文字列がセットされるのか気になったので、確認してみます。
src/test/groovy/geb/gebspec/inquiry/InquiryTestSpec.groovy に以下のテストを記述して、
def "maxlength を超えた文字列は入力できない?"() { setup: to InquiryInput01Page // lastname の maxlength は 20 なので 21文字セットしてみる $("#lastname").value("あ" * 20 + "い") expect: $("#lastname") == "あ" * 20 + "い" }
テストを実行すると失敗しました。21文字目の "い" がセットされていませんでした。
maxlength を超える文字列はセットできないようです。
FormModule クラスの setValueList メソッドを値セット+Tab キークリックするよう変更する
src/test/groovy/geb/module/FormModule.groovy の setValueList メソッドは今は以下の実装ですが、
void setValueList(valueList) { valueList.each { $(it.key).value(it.value) } }
空のデータをセットしてエラーメッセージを表示する以下のテストを実行した時に、
def "テスト"() { setup: to InquiryInput01Page form.setValueList(initialValueList) Thread.sleep(10000) expect: true }
年齢に空文字列をセットしたのに、フォーカスが移動していないのでエラーメッセージが表示されていないことに気づきました。
src/test/groovy/geb/module/FormModule.groovy を以下のよう変更し、値をセットした後にTABキーをクリックして blur イベントが発生するようにします。
void setValueList(valueList) { valueList.each { $(it.key).value(it.value) $(it.key) << Keys.TAB } }
$(it.key) << Keys.TAB
を追加します。
これでさっきのテストを実行すると、今度は年齢に空文字列をセットしてTABキーがクリックされるのでエラーメッセージが表示されます。
次回は。。。
Geb を使用したテストの作り方が簡単ながらも分かったので一旦ここで終了します。入力画面3に進む前に少し迷走する予定です。。。
履歴
2017/11/24
初版発行。