かんがるーさんの日記

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

Spring Boot + npm + Geb で入力フォームを作ってテストする ( その71 )( Geb で入力画面1~3→確認画面→完了画面を通したテストを作成する )

概要

記事一覧はこちらです。

Spring Boot + npm + Geb で入力フォームを作ってテストする ( その70 )( 完了画面を作成する ) の続きです。

  • 今回の手順で確認できるのは以下の内容です。
    • Geb を利用して、入力画面1~3→確認画面→完了画面を通したテストを作成します。
    • テストでは DB のデータやメールの送信内容も確認します。

参照したサイト・書籍

目次

  1. InquiryInput01Page クラスを変更する
  2. InquiryInput02Page クラスを変更する
  3. InquiryInput03Page クラスを作成する
  4. InquiryConfirmPage クラスを作成する
  5. InquiryCompletePage クラスを作成する
  6. FormModule クラスを変更する
  7. inquirymail-body_003.txt を追加する
  8. InquiryTestSpec クラスに入力画面1~3→確認画面→完了画面を通したテストを追加する
  9. 動作確認
  10. 全ての Geb のテストを実行してみる

手順

InquiryInput01Page クラスを変更する

src/test/groovy/geb/page/inquiry/InquiryInput01Page.groovy に今回使用するテストデータを追加します。

class InquiryInput01Page extends Page {
    ..........

    static valueList01 = [
            "#lastname"        : "田中",
            "#firstname"       : "太郎",
            "#lastkana"        : "たなか",
            "#firstkana"       : "たろう",
            "input[name='sex']": SexValues.MALE.value,
            "#age"             : "30",
            "#job"             : JobValues.EMPLOYEE.value,
    ]

}
  • static valueList01 = [ ... ] を追加します。

InquiryInput02Page クラスを変更する

src/test/groovy/geb/page/inquiry/InquiryInput02Page.groovy に今回使用するテストデータを追加します。

class InquiryInput02Page extends Page {
    ..........

    static valueList01 = [
            "#zipcode1": "100",
            "#zipcode2": "0005",
            "#address" : "東京都千代田区飯田橋1-1",
            "#tel1"    : "03",
            "#tel2"    : "1234",
            "#tel3"    : "5678",
            "#email"   : "taro.tanaka@sample.co.jp",
    ]

}

InquiryInput03Page クラスを作成する

src/test/groovy/geb/page/inquiry の下に InquiryInput03Page.groovy を新規作成し、以下の内容を記述します。

package geb.page.inquiry

import geb.Page
import geb.module.FormModule
import ksbysample.webapp.bootnpmgeb.values.Type1Values
import ksbysample.webapp.bootnpmgeb.values.Type2Values

class InquiryInput03Page extends Page {

    static url = "/inquiry/input/03"
    static at = { title == "入力フォーム - 入力画面3" }
    static content = {
        form { module FormModule }
    }

    static initialValueList = [
            "#type1"              : "",
            "input[name='type2']" : null,
            "#inquiry"            : "",
            "input[name='survey']": null,
    ]

    static valueList01 = [
            "#type1"              : Type1Values.PRODUCT.value,
            "input[name='type2']" : [
                    Type2Values.CATALOGUE.value,
                    Type2Values.ESTIMATE.value,
                    Type2Values.OTHER.value,
            ],
            "#inquiry"            : "これは\nテスト\nです",
            "input[name='survey']": ["1", "2", "3", "4", "5", "6", "7", "8"],
    ]

}

InquiryConfirmPage クラスを作成する

src/test/groovy/geb/page/inquiry の下に InquiryConfirmPage.groovy を新規作成し、以下の内容を記述します。

package geb.page.inquiry

import geb.Page
import geb.module.FormModule
import ksbysample.webapp.bootnpmgeb.values.JobValues
import ksbysample.webapp.bootnpmgeb.values.SexValues
import ksbysample.webapp.bootnpmgeb.values.Type1Values
import ksbysample.webapp.bootnpmgeb.values.Type2Values

import java.util.stream.Collectors

class InquiryConfirmPage extends Page {

    static url = "/inquiry/confirm"
    static at = { title == "入力フォーム - 確認画面" }
    static content = {
        form { module FormModule }
    }

    static textList01 = [
            "#name"   : "田中 太郎",
            "#kana"   : "たなか たろう",
            "#sex"    : SexValues.MALE.text,
            "#age"    : "30 歳",
            "#job"    : JobValues.EMPLOYEE.text,
            "#zipcode": "〒 100-0005",
            "#address": "東京都千代田区飯田橋1-1",
            "#tel"    : "03-1234-5678",
            "#email"  : "taro.tanaka@sample.co.jp",
            "#type1"  : Type1Values.PRODUCT.text,
            "#type2"  : [Type2Values.ESTIMATE.text
                         , Type2Values.CATALOGUE.text
                         , Type2Values.OTHER.text].stream()
                    .collect(Collectors.joining("、")),
            "#inquiry": "これは\nテスト\nです",
    ]

}

InquiryCompletePage クラスを作成する

src/test/groovy/geb/page/inquiry の下に InquiryCompletePage.groovy を新規作成し、以下の内容を記述します。

package geb.page.inquiry

import geb.Page
import geb.module.FormModule
import org.openqa.selenium.By

class InquiryCompletePage extends Page {

    static url = "/inquiry/confirm"
    static at = { title == "入力フォーム - 完了画面" }
    static content = {
        form { module FormModule }
        btnToInput01 { $(By.xpath("//button[contains(text(), '入力画面へ')]")) }
    }

}

FormModule クラスを変更する

src/test/groovy/geb/module/FormModule.groovy の以下の点を変更します。

class FormModule extends Module {

    static content = {
        btnBack { $(".js-btn-back") }
        btnNext { $(".js-btn-next") }
        btnConfirm { $(".js-btn-confirm") }
        btnInput01 { $(".js-btn-input01") }
        btnSend { $(".js-btn-send") }
    }

    ..........

    /**
     * セレクタに値がセットされているかを検証する
     * このメソッドは Spock の then, expect で使用する想定である
     *
     * @param valueList 検証するセレクタと値を記述した Map
     * @return true 固定
     */
    boolean assertValueList(valueList) {
        valueList.each { key, value ->
            if ($(key).first().attr("type") == "radio") {
                WebElement element = $(key).allElements().find { it.selected }
                if (element == null) {
                    assert null == value
                } else {
                    assert element.getAttribute("value") == value
                }
            } else if ($(key).first().attr("type") == "checkbox") {
                value.each { v ->
                    WebElement element = $(key).allElements().find {
                        el -> el.getAttribute("value") == v && el.selected
                    }
                    assert element != null
                }
            } else {
                assert $(key).value() == value
            }
        }
        true
    }

    /**
     * セレクタにテキストがセットされているかを検証する
     * このメソッドは Spock の then, expect で使用する想定である
     *
     * @param textList 検証するセレクタと値を記述した Map
     * @return true 固定
     */
    boolean assertTextList(textList) {
        textList.each { key, value ->
            assert $(key).text() == value
        }
        true
    }

}
  • static content = { ... } 内に以下のボタンの定義を追加します。
    • btnConfirm
    • btnInput01
    • btnSend
  • assertValueList メソッド内に else if ($(key).first().attr("type") == "checkbox") { ... } の処理を追加します。
  • assertTextList メソッドを追加します。

inquirymail-body_003.txt を追加する

メールの本文検証用のファイルを追加します。src/test/resources/ksbysample/webapp/bootnpmgeb/web/inquiry の下に inquirymail-body_003.txt を追加し、以下の内容を記述します。

問い合わせフォームから入力された内容は以下の通りです。

お名前(漢字):田中 太郎
お名前(かな):たなか たろう
性別:男性
年齢:30歳
職業:会社員
郵便番号:〒100-0005
住所:東京都千代田区飯田橋1-1
電話番号:03-1234-5678
メールアドレス:taro.tanaka@sample.co.jp

お問い合わせの種類1:製品に関するお問い合わせ
お問い合わせの種類2:見積が欲しい、資料が欲しい、その他の問い合わせ
お問い合わせの内容:
これは
テスト
です
アンケート:
・選択肢1だけ長くしてみる
・選択肢2
・選択肢3
・選択肢4
・選択肢5が少し長い
・選択肢6
・選択肢7
・8

InquiryTestSpec クラスに入力画面1~3→確認画面→完了画面を通したテストを追加する

src/test/groovy/geb/gebspec/inquiry/InquiryTestSpec.groovy の以下の点を変更します。

class InquiryTestSpec extends GebSpec {

    @Rule
    MailServerResource mailServerResource = new MailServerResource()

    Sql sql

    def setup() {
        // 外部プロセスから接続するので H2 TCP サーバへ接続する
        sql = Sql.newInstance("jdbc:h2:tcp://localhost:9092/mem:bootnpmgebdb", "sa", "")
        sql.execute("truncate table INQUIRY_DATA")
    }

    def teardown() {
        sql.close()
    }

    ..........

    def "入力画面1~3→確認画面→完了画面の全ての画面を通す"() {
        setup: "入力画面1を表示する"
        to InquiryInput01Page

        and: "データを入力して次へボタンをクリックし、入力画面2へ遷移する"
        form.setValueList(valueList01)
        form.btnNext.click(InquiryInput02Page)

        and: "データを入力して次へボタンをクリックし、入力画面3へ遷移する"
        form.setValueList(valueList01)
        form.btnNext.click(InquiryInput03Page)

        and: "データを入力して次へボタンをクリックし、確認画面へ遷移する"
        form.setValueList(valueList01)
        form.btnConfirm.click(InquiryConfirmPage)

        and: "確認画面にデータが表示されていることを確認する"
        form.assertTextList(textList01)
        $("#survey > ul > li").count(8)
        $("#survey > ul > li", 0).text() == "選択肢1だけ長くしてみる"
        $("#survey > ul > li", 7).text() == "8"

        and: "修正するボタンをクリックし、入力画面1へ戻る"
        form.btnInput01.click(InquiryInput01Page)
        form.assertValueList(valueList01)

        and: "次へボタンをクリックし、入力画面2へ遷移する"
        form.btnNext.click(InquiryInput02Page)
        form.assertValueList(valueList01)

        and: "次へボタンをクリックし、入力画面3へ遷移する"
        form.btnNext.click(InquiryInput03Page)
        form.assertValueList(valueList01)

        and: "次へボタンをクリックし、確認画面へ遷移する"
        form.btnConfirm.click(InquiryConfirmPage)
        form.assertTextList(textList01)
        $("#survey > ul > li").count(8)
        $("#survey > ul > li", 0).text() == "選択肢1だけ長くしてみる"
        $("#survey > ul > li", 7).text() == "8"

        expect: "送信するボタンをクリックし、完了画面へ遷移する"
        form.btnSend.click(InquiryCompletePage)

        // INQUIRY_DATA テーブルに1件データが登録されていることを確認する
        def rows = sql.rows("SELECT * FROM INQUIRY_DATA")
        rows.size() == 1
        rows[0]["lastname"] == "田中"
        rows[0]["firstname"] == "太郎"
        rows[0]["lastkana"] == "たなか"
        rows[0]["firstkana"] == "たろう"
        rows[0]["sex"] == SexValues.MALE.value
        rows[0]["age"] == 30
        rows[0]["job"] == JobValues.EMPLOYEE.value
        rows[0]["zipcode1"] == "100"
        rows[0]["zipcode2"] == "0005"
        rows[0]["address"] == "東京都千代田区飯田橋1-1"
        rows[0]["tel1"] == "03"
        rows[0]["tel2"] == "1234"
        rows[0]["tel3"] == "5678"
        rows[0]["email"] == "taro.tanaka@sample.co.jp"
        rows[0]["type1"] == Type1Values.PRODUCT.value
        rows[0]["type2"] == [Type2Values.ESTIMATE.value
                             , Type2Values.CATALOGUE.value
                             , Type2Values.OTHER.value].stream()
                .collect(Collectors.joining(","))
        rows[0]["inquiry"].asciiStream.text == "これは\r\nテスト\r\nです"
        rows[0]["survey"] == "1,2,3,4,5,6,7,8"

        // メールが1件送信されていることを確認する
        mailServerResource.messagesCount == 1
        MimeMessage message = mailServerResource.firstMessage
        message.subject == "問い合わせフォームからお問い合わせがありました"
        message.content == new ClassPathResource("ksbysample/webapp/bootnpmgeb/web/inquiry/inquirymail-body_003.txt").inputStream.text

        and: "再び入力画面1を表示する"
        btnToInput01.click(InquiryInput01Page)
        // 入力したデータは表示されていない
        form.assertValueList(initialValueList)
    }

}
  • 以下のフィールドを追加する。
    • @Rule MailServerResource mailServerResource = new MailServerResource()
    • Sql sql
  • setup()teardown() を追加する。
  • def "入力画面1~3→確認画面→完了画面の全ての画面を通す"() { ... } を追加する。

動作確認

まずは Firefox でテストします。GebConfig.groovy は以下の設定です。

System.setProperty("webdriver.gecko.driver", "C:/geckodriver/0.19.0/geckodriver.bat")
System.setProperty("webdriver.chrome.driver", "C:/chromedriver/2.33/chromedriver.exe")
driver = {
    FirefoxOptions firefoxOptions = new FirefoxOptions()
    firefoxOptions.setHeadless(true)
    new FirefoxDriver(firefoxOptions)
}

Tomcat を起動した後、src/test/groovy/geb/gebspec/inquiry/InquiryTestSpec.groovy の "入力画面1~3→確認画面→完了画面の全ての画面を通す"() テストメソッド左側の矢印から Run '入力画面1~3→確認画面→完了画面の...()' をクリックします。

f:id:ksby:20180722015332p:plain

テストが実行されて成功しました。

f:id:ksby:20180722020322p:plain

次は Chrome でテストするために、GebConfig.groovy を以下のように変更します。

System.setProperty("webdriver.gecko.driver", "C:/geckodriver/0.19.0/geckodriver.bat")
System.setProperty("webdriver.chrome.driver", "C:/chromedriver/2.33/chromedriver.exe")
driver = {
//    FirefoxOptions firefoxOptions = new FirefoxOptions()
//    firefoxOptions.setHeadless(true)
//    new FirefoxDriver(firefoxOptions)
    ChromeOptions chromeOptions = new ChromeOptions()
    chromeOptions.setHeadless(true)
    new ChromeDriver(chromeOptions)
}

Firefox の時と同様に Run '入力画面1~3→確認画面→完了画面の...()' をクリックすると、テストは実行されましたが今度は失敗しました。入力項目に値がセットできていないようです。

f:id:ksby:20180722020624p:plain

ChromeDrive の Downloads ページ を見ると 2.40 がリリースされていましたので入れ替えます。Supports Chrome v66-68 のようにサポートしている Chrome のバージョンが記載されており、2.33 だと Supports Chrome v60-62 で使用している Chrome とバージョンが合っていませんでした(現時点の Chrome のバージョンは 67)。

ChromeDriver 2.40 のリンクをクリックして、その先にある chromedriver_win32.zip をダウンロードします。ダウンロード後、解凍して作成された chromedriver.exe を C:\chromedriver\2.40 の下に配置します。

GebConfig.groovy の chromedriver.exe のパスを変更してから、

System.setProperty("webdriver.gecko.driver", "C:/geckodriver/0.19.0/geckodriver.bat")
System.setProperty("webdriver.chrome.driver", "C:/chromedriver/2.40/chromedriver.exe")

テストを実行すると今度は成功しました。

f:id:ksby:20180722021646p:plain

全ての Geb のテストを実行してみる

今度は Geb の全てのテストを実行してみます。Gradle Tool Window から firefoxTest タスクを実行します。

f:id:ksby:20180722090807p:plain

半数以上失敗しましたね。。。 失敗した原因を確認したところ、以下の内容でした。

  • InquiryInput01Page クラスの maxLengthValueList に全ての項目が記述されていないので、値がセットされない入力項目がある。
  • InquiryInput01Page クラスの maxLengthValueList で "#lastkana""#firstkana" にカナの値が設定されていなかったので、入力チェックエラーが発生していた。

src/test/groovy/geb/page/inquiry/InquiryInput01Page.groovy の maxLengthValueList を以下のように変更します。

    static maxLengthValueList = [
            "#lastname"        : "田" * 20,
            "#firstname"       : "太" * 20,
            "#lastkana"        : "あ" * 20,
            "#firstkana"       : "い" * 20,
            "input[name='sex']": SexValues.MALE.value,
            "#age"             : "9" * 3,
            "#job"             : JobValues.EMPLOYEE.value,
    ]

また maxLengthValueList で全ての値をセットするように変更したので、src/test/groovy/geb/gebspec/inquiry/InquiryTestSpec.groovy から以下の記述の行を全て取り除きます。

  • form.setValue("input[name='sex']", "1")
  • $("#inquiryInput01Form").sex == "1"

再度 firefoxTest タスクを実行すると今度は全て成功しました。

f:id:ksby:20180722103908p:plain

次に chromeTest タスクを実行してみると、こちらも全て成功しました。

f:id:ksby:20180722104232p:plain

最後に gebTest タスクを実行して全て成功することを確認します。

f:id:ksby:20180722104913p:plain

履歴

2018/07/22
初版発行。
2018/07/22
* Geb のテストのために Spring Boot のアプリを起動しているのは develop profile で unittest profile でないことに気づいたのと、unittest profile でも H2 TCP サーバを起動させると一部のテストが失敗するようになったので、「H2DatabaseConfig クラスを変更し、unittest profile でも H2 TCPサーバを起動させる」を削除しました。