かんがるーさんの日記

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

Spring Boot + npm + Geb で入力フォームを作ってテストする ( その63 )( MockMvc#perform 呼び出し時に .with(csrf()) を付けていなくてもテストが成功していた理由とは? )

概要

記事一覧はこちらです。

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

  • 今回の手順で確認できるのは以下の内容です。
    • 確認画面のテストを作成し終えた時に、今回 Spring Security を入れて CSRF対策も入っているはずなのに MockMvc#perform 呼び出し時に .with(csrf()) を付けていないのにテストが成功しているというおかしな事態になっていることに気づいたので、その原因を調査します。

参照したサイト・書籍

目次

  1. 単に .apply(springSecurity()) を付け忘れいてただけでした。。。
  2. InquiryInputControllerTest クラスを修正する
  3. InquiryConfirmControllerTest クラスを修正する

手順

単に .apply(springSecurity()) を付け忘れいてただけでした。。。

Spring Security Reference - 12. Spring MVC Test Integration を見たら1発で原因が分かりました。単なる .apply(springSecurity()) の付け忘れでした。。。

ということで、入力画面・確認画面のテストで MockMvc のインスタンス生成時に .apply(springSecurity()) を付けるよう修正して、テストを見直すことにします。

InquiryInputControllerTest クラスを修正する

最初に MockMvc のインスタンス生成時に .apply(springSecurity()) を付けるよう修正します。

        @Before
        void setup() {
            mockMvc = MockMvcBuilders.webAppContextSetup(context)
                    .apply(springSecurity())
                    .build()
        }

全てのテストを実行すると、今度は 403 のエラーが出るようになりました。

f:id:ksby:20180708161507p:plain

テスト内で POST でリクエストを送信しているところを TestHelper.postForm(...)TestHelper.postForm(...).with(csrf()) のように修正します。

再度全てのテストを実行すると、テストを実装していない(必ず失敗する)1件を除き成功しました。

f:id:ksby:20180708162618p:plain

InquiryConfirmControllerTest クラスを修正する

こちらも MockMvc のインスタンス生成時に .apply(springSecurity()) を付けるよう修正します。意識していませんでしたが、InquiryInputControllerTest は JUnit4+Groovy で書いていますが、InquiryConfirmControllerTest は Spock+Groovy で書いていました。

        def setup() {
            mockMvc = MockMvcBuilders.webAppContextSetup(context)
                    .apply(springSecurity())
                    .build()
            sql = new Sql(dataSource)
            sql.execute("truncate table INQUIRY_DATA")
        }

全てのテストを実行すると、こちらも 403 のエラーが出るようになりました。

f:id:ksby:20180708163828p:plain

テスト内で POST でリクエストを送信しているところを TestHelper.postForm(...)TestHelper.postForm(...).with(csrf()) のように修正します。

再度全てのテストを実行すると、今度は成功しました。

f:id:ksby:20180708164229p:plain

最後に修正後のソースコードを載せておきます。

ソースコード

InquiryInputControllerTest.groovy

package ksbysample.webapp.bootnpmgeb.web.inquiry

import ksbysample.common.test.helper.TestHelper
import ksbysample.webapp.bootnpmgeb.values.Type1Values
import ksbysample.webapp.bootnpmgeb.values.Type2Values
import ksbysample.webapp.bootnpmgeb.web.inquiry.form.InquiryInput01Form
import ksbysample.webapp.bootnpmgeb.web.inquiry.form.InquiryInput02Form
import ksbysample.webapp.bootnpmgeb.web.inquiry.form.InquiryInput03Form
import org.junit.Before
import org.junit.Test
import org.junit.experimental.runners.Enclosed
import org.junit.runner.RunWith
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.mock.web.MockHttpSession
import org.springframework.test.context.junit4.SpringRunner
import org.springframework.test.web.servlet.MockMvc
import org.springframework.test.web.servlet.MvcResult
import org.springframework.test.web.servlet.setup.MockMvcBuilders
import org.springframework.web.context.WebApplicationContext
import org.yaml.snakeyaml.Yaml

import static ksbysample.common.test.matcher.HtmlResultMatchers.html
import static org.assertj.core.api.Assertions.catchThrowable
import static org.assertj.core.api.AssertionsForClassTypes.assertThat
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf
import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrlPattern
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status

@RunWith(Enclosed)
class InquiryInputControllerTest {

    @RunWith(SpringRunner)
    @SpringBootTest
    static class 入力画面1のテスト {
        private InquiryInput01Form inquiryInput01Form_001 =
                (InquiryInput01Form) new Yaml().load(getClass().getResourceAsStream("InquiryInput01Form_001.yaml"))

        @Autowired
        private WebApplicationContext context

        MockMvc mockMvc

        @Before
        void setup() {
            mockMvc = MockMvcBuilders.webAppContextSetup(context)
                    .apply(springSecurity())
                    .build()
        }

        @Test
        void "初期表示時は画面の項目には何もセットされない"() {
            expect:
            mockMvc.perform(get("/inquiry/input/01"))
                    .andExpect(status().isOk())
                    .andExpect(html("#lastname").val(""))
                    .andExpect(html("#firstname").val(""))
                    .andExpect(html("#lastkana").val(""))
                    .andExpect(html("#firstkana").val(""))
                    .andExpect(html("input[name='sex'][checked='checked']").notExists())
                    .andExpect(html("#age").val(""))
                    .andExpect(html("select[name='job'] option[selected]").notExists())
        }

        @Test
        void "項目全てに入力して入力画面2へ遷移してから戻ると以前入力したデータがセットされて表示される"() {
            expect: "項目全てに入力して「次へ」ボタンをクリックする"
            MvcResult result = mockMvc.perform(
                    TestHelper.postForm("/inquiry/input/01?move=next", inquiryInput01Form_001).with(csrf()))
                    .andExpect(status().isFound())
                    .andExpect(redirectedUrlPattern("**/inquiry/input/02"))
                    .andReturn()
            MockHttpSession session = result.getRequest().getSession()

            and: "再び入力画面1を表示する"
            mockMvc.perform(get("/inquiry/input/01").session(session))
                    .andExpect(status().isOk())
                    .andExpect(html("#lastname").val(inquiryInput01Form_001.lastname))
                    .andExpect(html("#firstname").val(inquiryInput01Form_001.firstname))
                    .andExpect(html("#lastkana").val(inquiryInput01Form_001.lastkana))
                    .andExpect(html("#firstkana").val(inquiryInput01Form_001.firstkana))
                    .andExpect(html("input[name='sex'][checked='checked']").val(inquiryInput01Form_001.sex))
                    .andExpect(html("#age").val(inquiryInput01Form_001.age))
                    .andExpect(html("select[name='job'] option[selected]").val(inquiryInput01Form_001.job))
        }

        @Test
        void "入力チェックエラーのあるデータで入力画面2へ遷移しようとするとIllegalArgumentExceptionが発生する"() {
            setup: "入力チェックエラーになるデータを用意する"
            inquiryInput01Form_001.lastname = "x" * 21

            expect: "入力画面1の「次へ」ボタンをクリックする"
            Throwable thrown = catchThrowable({
                mockMvc.perform(
                        TestHelper.postForm("/inquiry/input/01?move=next", inquiryInput01Form_001).with(csrf()))
                        .andExpect(status().isOk())
            })
            assertThat(thrown.cause).isInstanceOf(IllegalArgumentException)
        }

    }

    @RunWith(SpringRunner)
    @SpringBootTest
    static class 入力画面2のテスト {
        private InquiryInput01Form inquiryInput01Form_001 =
                (InquiryInput01Form) new Yaml().load(getClass().getResourceAsStream("InquiryInput01Form_001.yaml"))
        private InquiryInput02Form inquiryInput02Form_001 = new InquiryInput02Form(
                zipcode1: "102"
                , zipcode2: "0072"
                , address: "東京都千代田区飯田橋1-1"
                , tel1: "03"
                , tel2: "1234"
                , tel3: "5678"
                , email: "taro.tanaka@sample.co.jp")

        @Autowired
        private WebApplicationContext context

        MockMvc mockMvc

        @Before
        void setup() {
            mockMvc = MockMvcBuilders.webAppContextSetup(context)
                    .build()
        }

        @Test
        void "初期表示時は画面の項目には何もセットされない"() {
            expect:
            mockMvc.perform(get("/inquiry/input/02"))
                    .andExpect(status().isOk())
                    .andExpect(html("#zipcode1").val(""))
                    .andExpect(html("#zipcode2").val(""))
                    .andExpect(html("#address").val(""))
                    .andExpect(html("#tel1").val(""))
                    .andExpect(html("#tel2").val(""))
                    .andExpect(html("#tel3").val(""))
                    .andExpect(html("#email").val(""))
        }

        @Test
        void "項目全てに入力して前の画面へ戻るボタンをクリックすると入力画面1へ戻り、次へ戻るボタンを押して入力画面2へ戻ると以前入力したデータがセットされて表示される"() {
            when: "入力画面1で項目全てに入力して「次へ」ボタンをクリックする"
            MvcResult result = mockMvc.perform(
                    TestHelper.postForm("/inquiry/input/01?move=next", inquiryInput01Form_001).with(csrf()))
                    .andExpect(status().isFound())
                    .andExpect(redirectedUrlPattern("**/inquiry/input/02"))
                    .andReturn()
            MockHttpSession session = result.getRequest().getSession()

            and: "入力画面2で項目全てに入力して「前の画面へ戻る」ボタンをクリックする"
            mockMvc.perform(TestHelper.postForm("/inquiry/input/02?move=back", inquiryInput02Form_001).with(csrf()).session(session))
                    .andExpect(status().isFound())
                    .andExpect(redirectedUrlPattern("**/inquiry/input/01"))

            and: "入力画面1で「次へ」ボタンをクリックする"
            mockMvc.perform(TestHelper.postForm("/inquiry/input/01?move=next", inquiryInput01Form_001).with(csrf()).session(session))
                    .andExpect(status().isFound())
                    .andExpect(redirectedUrlPattern("**/inquiry/input/02"))

            then: "入力画面2が以前入力したデータがセットされて表示される"
            mockMvc.perform(get("/inquiry/input/02").session(session))
                    .andExpect(status().isOk())
                    .andExpect(html("#zipcode1").val(inquiryInput02Form_001.zipcode1))
                    .andExpect(html("#zipcode2").val(inquiryInput02Form_001.zipcode2))
                    .andExpect(html("#address").val(inquiryInput02Form_001.address))
                    .andExpect(html("#tel1").val(inquiryInput02Form_001.tel1))
                    .andExpect(html("#tel2").val(inquiryInput02Form_001.tel2))
                    .andExpect(html("#tel3").val(inquiryInput02Form_001.tel3))
                    .andExpect(html("#email").val(inquiryInput02Form_001.email))
        }

        @Test
        void "項目全てに入力して次へボタンをクリックすると入力画面3へ遷移し、前の画面へ戻るボタンを押して入力画面2へ戻ると以前入力したデータがセットされて表示される"() {
            when: "入力画面2で項目全てに入力して「次へ」ボタンをクリックする"
            MvcResult result = mockMvc.perform(TestHelper.postForm("/inquiry/input/02?move=next", inquiryInput02Form_001).with(csrf()))
                    .andExpect(status().isFound())
                    .andExpect(redirectedUrlPattern("**/inquiry/input/03"))
                    .andReturn()
            MockHttpSession session = result.getRequest().getSession()

            and: "入力画面3で「前の画面へ戻る」ボタンをクリックする"
            mockMvc.perform(TestHelper.postForm("/inquiry/input/03?move=back", inquiryInput01Form_001).with(csrf()).session(session))
                    .andExpect(status().isFound())
                    .andExpect(redirectedUrlPattern("**/inquiry/input/02"))

            then: "入力画面2が以前入力したデータがセットされて表示される"
            mockMvc.perform(get("/inquiry/input/02").session(session))
                    .andExpect(status().isOk())
                    .andExpect(html("#zipcode1").val(inquiryInput02Form_001.zipcode1))
                    .andExpect(html("#zipcode2").val(inquiryInput02Form_001.zipcode2))
                    .andExpect(html("#address").val(inquiryInput02Form_001.address))
                    .andExpect(html("#tel1").val(inquiryInput02Form_001.tel1))
                    .andExpect(html("#tel2").val(inquiryInput02Form_001.tel2))
                    .andExpect(html("#tel3").val(inquiryInput02Form_001.tel3))
                    .andExpect(html("#email").val(inquiryInput02Form_001.email))
        }

        @Test
        void "入力チェックエラーのあるデータで「前の画面へ戻る」ボタンをクリックするとIllegalArgumentExceptionが発生する"() {
            setup: "入力チェックエラーになるデータを用意する"
            inquiryInput02Form_001.zipcode1 = "1"

            expect: "入力画面2の「前の画面へ戻る」ボタンをクリックする"
            Throwable thrown = catchThrowable({
                mockMvc.perform(
                        TestHelper.postForm("/inquiry/input/02?move=back", inquiryInput02Form_001).with(csrf()))
                        .andExpect(status().isOk())
            })
            assertThat(thrown.cause).isInstanceOf(IllegalArgumentException)
        }

        @Test
        void "入力チェックエラーのあるデータで「次へ」ボタンをクリックするとIllegalArgumentExceptionが発生する"() {
            setup: "入力チェックエラーになるデータを用意する"
            inquiryInput02Form_001.zipcode1 = "1"

            expect: "入力画面2の「次へ」ボタンをクリックする"
            Throwable thrown = catchThrowable({
                mockMvc.perform(
                        TestHelper.postForm("/inquiry/input/02?move=next", inquiryInput02Form_001).with(csrf()))
                        .andExpect(status().isOk())
            })
            assertThat(thrown.cause).isInstanceOf(IllegalArgumentException)
        }

    }

    @RunWith(SpringRunner)
    @SpringBootTest
    static class 入力画面3のテスト {
        private InquiryInput01Form inquiryInput01Form_001 =
                (InquiryInput01Form) new Yaml().load(getClass().getResourceAsStream("InquiryInput01Form_001.yaml"))
        private InquiryInput02Form inquiryInput02Form_001 = new InquiryInput02Form(
                zipcode1: "102"
                , zipcode2: "0072"
                , address: "東京都千代田区飯田橋1-1"
                , tel1: "03"
                , tel2: "1234"
                , tel3: "5678"
                , email: "taro.tanaka@sample.co.jp")
        private InquiryInput03Form inquiryInput03Form_001 = new InquiryInput03Form(
                type1: Type1Values.PRODUCT.value
                , type2: [Type2Values.ESTIMATE.value, Type2Values.CATALOGUE.value, Type2Values.OTHER.value]
                , inquiry: "これはテストです"
                , survey: ["1", "2", "3", "4", "5", "6", "7", "8"])

        @Autowired
        private WebApplicationContext context

        MockMvc mockMvc

        @Before
        void setup() {
            mockMvc = MockMvcBuilders.webAppContextSetup(context)
                    .build()
        }

        @Test
        void "初期表示時は画面の項目には何もセットされない"() {
            expect:
            mockMvc.perform(get("/inquiry/input/03"))
                    .andExpect(status().isOk())
                    .andExpect(html("select[name='type1'] option[selected]").notExists())
                    .andExpect(html("input[name='type2'][checked='checked']").notExists())
                    .andExpect(html("#inquiry").val(""))
                    .andExpect(html("input[name='survey'][checked='checked']").notExists())
        }

        @Test
        void "項目全てに入力して前の画面へ戻るボタンをクリックすると入力画面2へ戻り、次へ戻るボタンを押して入力画面3へ戻ると以前入力したデータがセットされて表示される"() {
            when: "入力画面1で項目全てに入力して「次へ」ボタンをクリックする"
            MvcResult result = mockMvc.perform(
                    TestHelper.postForm("/inquiry/input/01?move=next", inquiryInput01Form_001).with(csrf()))
                    .andExpect(status().isFound())
                    .andExpect(redirectedUrlPattern("**/inquiry/input/02"))
                    .andReturn()
            MockHttpSession session = result.getRequest().getSession()

            and: "入力画面2で項目全てに入力して「次へ」ボタンをクリックする"
            mockMvc.perform(TestHelper.postForm("/inquiry/input/02?move=next", inquiryInput02Form_001).with(csrf()).session(session))
                    .andExpect(status().isFound())
                    .andExpect(redirectedUrlPattern("**/inquiry/input/03"))

            and: "入力画面3で項目全てに入力して「前の画面へ戻る」ボタンをクリックする"
            mockMvc.perform(TestHelper.postForm("/inquiry/input/03?move=back", inquiryInput03Form_001).with(csrf()).session(session))
                    .andExpect(status().isFound())
                    .andExpect(redirectedUrlPattern("**/inquiry/input/02"))

            and: "入力画面2で「次へ」ボタンをクリックする"
            mockMvc.perform(TestHelper.postForm("/inquiry/input/02?move=next", inquiryInput02Form_001).with(csrf()).session(session))
                    .andExpect(status().isFound())
                    .andExpect(redirectedUrlPattern("**/inquiry/input/03"))

            then: "入力画面3が以前入力したデータがセットされて表示される"
            mockMvc.perform(get("/inquiry/input/03").session(session))
                    .andExpect(status().isOk())
                    .andExpect(html("select[name='type1'] option[selected]").val(inquiryInput03Form_001.type1))
                    .andExpect(html("input[name='type2'][checked='checked']").count(3))
                    .andExpect(html("#inquiry").val(inquiryInput03Form_001.inquiry))
                    .andExpect(html("input[name='survey'][checked='checked']").count(8))
        }

        @Test
        void "項目全てに入力して次へボタンをクリックすると確認画面へ遷移し、前の画面へ戻るボタンを押して入力画面3へ戻ると以前入力したデータがセットされて表示される"() {
            expect:
            assert false, "確認画面を実装してからテストを作成する"
        }

        @Test
        void "入力チェックエラーのあるデータで「次へ」ボタンをクリックするとIllegalArgumentExceptionが発生する"() {
            setup: "入力チェックエラーになるデータを用意する"
            inquiryInput03Form_001.type1 = ""

            expect: "入力画面3の「次へ」ボタンをクリックする"
            Throwable thrown = catchThrowable({
                mockMvc.perform(
                        TestHelper.postForm("/inquiry/input/03?move=next", inquiryInput03Form_001).with(csrf()))
                        .andExpect(status().isOk())
            })
            assertThat(thrown.cause).isInstanceOf(IllegalArgumentException)
        }

    }

}

InquiryConfirmControllerTest.groovy

package ksbysample.webapp.bootnpmgeb.web.inquiry

import groovy.sql.Sql
import ksbysample.common.test.helper.TestHelper
import ksbysample.common.test.rule.mail.MailServerResource
import ksbysample.webapp.bootnpmgeb.dao.InquiryDataDao
import ksbysample.webapp.bootnpmgeb.entity.SurveyOptions
import ksbysample.webapp.bootnpmgeb.helper.db.SurveyOptionsHelper
import ksbysample.webapp.bootnpmgeb.values.*
import ksbysample.webapp.bootnpmgeb.web.inquiry.form.InquiryInput01Form
import ksbysample.webapp.bootnpmgeb.web.inquiry.form.InquiryInput02Form
import ksbysample.webapp.bootnpmgeb.web.inquiry.form.InquiryInput03Form
import org.apache.commons.lang3.StringUtils
import org.junit.Rule
import org.junit.experimental.runners.Enclosed
import org.junit.runner.RunWith
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.core.io.ClassPathResource
import org.springframework.http.MediaType
import org.springframework.mock.web.MockHttpSession
import org.springframework.test.web.servlet.MockMvc
import org.springframework.test.web.servlet.MvcResult
import org.springframework.test.web.servlet.setup.MockMvcBuilders
import org.springframework.web.context.WebApplicationContext
import org.yaml.snakeyaml.Yaml
import spock.lang.Specification

import javax.mail.internet.MimeMessage
import javax.sql.DataSource
import java.util.stream.Collectors

import static ksbysample.common.test.matcher.HtmlResultMatchers.html
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf
import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrlPattern
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status

@RunWith(Enclosed)
class InquiryConfirmControllerTest {

    @SpringBootTest
    static class 確認画面のテスト extends Specification {

        InquiryInput01Form inquiryInput01Form_001 =
                (InquiryInput01Form) new Yaml().load(getClass().getResourceAsStream("InquiryInput01Form_001.yaml"))
        InquiryInput02Form inquiryInput02Form_001 = new InquiryInput02Form(
                zipcode1: "102"
                , zipcode2: "0072"
                , address: "東京都千代田区飯田橋1-1"
                , tel1: "03"
                , tel2: "1234"
                , tel3: "5678"
                , email: "taro.tanaka@sample.co.jp")
        InquiryInput03Form inquiryInput03Form_001 = new InquiryInput03Form(
                type1: Type1Values.PRODUCT.value
                , type2: [Type2Values.ESTIMATE.value, Type2Values.CATALOGUE.value, Type2Values.OTHER.value]
                , inquiry: "これはテストです"
                , survey: ["1", "2", "3", "4", "5", "6", "7", "8"])

        @Rule
        MailServerResource mailServerResource = new MailServerResource()

        @Autowired
        WebApplicationContext context

        @Autowired
        DataSource dataSource

        @Autowired
        ValuesHelper vh

        @Autowired
        SurveyOptionsHelper soh

        @Autowired
        InquiryDataDao inquiryDataDao

        MockMvc mockMvc

        Sql sql

        def setup() {
            mockMvc = MockMvcBuilders.webAppContextSetup(context)
                    .apply(springSecurity())
                    .build()
            sql = new Sql(dataSource)
            sql.execute("truncate table INQUIRY_DATA")
        }

        def teardown() {
            sql.close()
        }

        def "入力画面1~3で全ての項目に入力した場合のテスト"() {
            when: "入力画面1で項目全てに入力して「次へ」ボタンをクリックする"
            MvcResult result = mockMvc.perform(TestHelper.postForm("/inquiry/input/01?move=next", inquiryInput01Form_001).with(csrf()))
                    .andExpect(status().isFound())
                    .andExpect(redirectedUrlPattern("**/inquiry/input/02"))
                    .andReturn()
            MockHttpSession session = result.getRequest().getSession()

            and: "入力画面2で項目全てに入力して「次へ」ボタンをクリックする"
            mockMvc.perform(TestHelper.postForm("/inquiry/input/02?move=next", inquiryInput02Form_001).with(csrf()).session(session))
                    .andExpect(status().isFound())
                    .andExpect(redirectedUrlPattern("**/inquiry/input/03"))

            and: "入力画面3で項目全てに入力して「次へ」ボタンをクリックする"
            mockMvc.perform(TestHelper.postForm("/inquiry/input/03?move=next", inquiryInput03Form_001).with(csrf()).session(session))
                    .andExpect(status().isFound())
                    .andExpect(redirectedUrlPattern("**/inquiry/confirm"))

            then: "確認画面に入力画面1~3で入力したデータが表示される"
            mockMvc.perform(get("/inquiry/confirm").session(session))
                    .andExpect(status().isOk())
                    .andExpect(html("#name").text(inquiryInput01Form_001.lastname + " " + inquiryInput01Form_001.firstname))
                    .andExpect(html("#kana").text(inquiryInput01Form_001.lastkana + " " + inquiryInput01Form_001.firstkana))
                    .andExpect(html("#sex").text(vh.getText(SexValues, inquiryInput01Form_001.sex)))
                    .andExpect(html("#age").text(inquiryInput01Form_001.age + " 歳"))
                    .andExpect(html("#job").text(vh.getText(JobValues, inquiryInput01Form_001.job)))
                    .andExpect(html("#zipcode").text("〒 " + inquiryInput02Form_001.zipcode1 + "-" + inquiryInput02Form_001.zipcode2))
                    .andExpect(html("#address").text(inquiryInput02Form_001.address))
                    .andExpect(html("#tel").text(inquiryInput02Form_001.tel1 + "-" + inquiryInput02Form_001.tel2 + "-" + inquiryInput02Form_001.tel3))
                    .andExpect(html("#email").text(inquiryInput02Form_001.email))
                    .andExpect(html("#type1").text(vh.getText(Type1Values, inquiryInput03Form_001.type1)))
                    .andExpect(html("#type2").text([Type2Values.ESTIMATE.text
                                                    , Type2Values.CATALOGUE.text
                                                    , Type2Values.OTHER.text].stream().collect(Collectors.joining("、"))))
                    .andExpect(html("#inquiry").text(inquiryInput03Form_001.inquiry))
                    .andExpect(html("#survey > ul > li").count(8))
                    .andExpect(html("#survey > ul > li:nth-of-type(1)").text(
                    soh.selectItemList("survey").stream()
                            .filter({ SurveyOptions surveyOptions -> StringUtils.equals(surveyOptions.itemValue, "1") })
                            .map { SurveyOptions surveyOptions -> surveyOptions.itemName }
                            .findFirst().get()))

            and: "確認画面で「送信」ボタンをクリックする"
            mockMvc.perform(post("/inquiry/confirm/send").contentType(MediaType.APPLICATION_FORM_URLENCODED).with(csrf()).session(session))
                    .andExpect(status().isFound())
                    .andExpect(redirectedUrlPattern("**/inquiry/complete/"))

            then: "DBとメールを確認する"
            def rows = sql.rows("SELECT * FROM INQUIRY_DATA")
            rows.size() == 1
            rows[0]["lastname"] == "12345678901234567890"
            rows[0]["firstname"] == "12345678901234567890"
            rows[0]["lastkana"] == "あいうえおかきくけこさしすせそたちつてと"
            rows[0]["firstkana"] == "なにぬねのはひふへほまみむめもあいうえお"
            rows[0]["sex"] == "2"
            rows[0]["age"] == 999
            rows[0]["job"] == "3"
            rows[0]["zipcode1"] == "102"
            rows[0]["zipcode2"] == "0072"
            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"] == "1"
            rows[0]["type2"] == "1,2,3"
            rows[0]["inquiry"].asciiStream.text == inquiryInput03Form_001.inquiry
            rows[0]["survey"] == "1,2,3,4,5,6,7,8"

            mailServerResource.messagesCount == 1
            MimeMessage message = mailServerResource.firstMessage
            message.subject == "問い合わせフォームからお問い合わせがありました"
            message.content == new ClassPathResource("ksbysample/webapp/bootnpmgeb/web/inquiry/inquirymail-body_001.txt").inputStream.text
        }

        def "入力画面1~3で必須項目だけに入力した場合のテスト"() {
            when: "入力画面1で必須項目だけに入力して「次へ」ボタンをクリックする"
            inquiryInput01Form_001.job = StringUtils.EMPTY
            MvcResult result = mockMvc.perform(TestHelper.postForm("/inquiry/input/01?move=next", inquiryInput01Form_001).with(csrf()))
                    .andExpect(status().isFound())
                    .andExpect(redirectedUrlPattern("**/inquiry/input/02"))
                    .andReturn()
            MockHttpSession session = result.getRequest().getSession()

            and: "入力画面2で必須項目だけに入力して「次へ」ボタンをクリックする"
            inquiryInput02Form_001.tel1 = StringUtils.EMPTY
            inquiryInput02Form_001.tel2 = StringUtils.EMPTY
            inquiryInput02Form_001.tel3 = StringUtils.EMPTY
            mockMvc.perform(TestHelper.postForm("/inquiry/input/02?move=next", inquiryInput02Form_001).with(csrf()).session(session))
                    .andExpect(status().isFound())
                    .andExpect(redirectedUrlPattern("**/inquiry/input/03"))

            and: "入力画面3で必須項目だけに入力して「次へ」ボタンをクリックする"
            inquiryInput03Form_001.survey = []
            mockMvc.perform(TestHelper.postForm("/inquiry/input/03?move=next", inquiryInput03Form_001).with(csrf()).session(session))
                    .andExpect(status().isFound())
                    .andExpect(redirectedUrlPattern("**/inquiry/confirm"))

            then: "確認画面に入力画面1~3で入力したデータが表示される"
            mockMvc.perform(get("/inquiry/confirm").session(session))
                    .andExpect(status().isOk())
                    .andExpect(html("#name").text(inquiryInput01Form_001.lastname + " " + inquiryInput01Form_001.firstname))
                    .andExpect(html("#kana").text(inquiryInput01Form_001.lastkana + " " + inquiryInput01Form_001.firstkana))
                    .andExpect(html("#sex").text(vh.getText(SexValues, inquiryInput01Form_001.sex)))
                    .andExpect(html("#age").text(inquiryInput01Form_001.age + " 歳"))
                    .andExpect(html("#job").text(StringUtils.EMPTY))
                    .andExpect(html("#zipcode").text("〒 " + inquiryInput02Form_001.zipcode1 + "-" + inquiryInput02Form_001.zipcode2))
                    .andExpect(html("#address").text(inquiryInput02Form_001.address))
                    .andExpect(html("#tel").text(StringUtils.EMPTY))
                    .andExpect(html("#email").text(inquiryInput02Form_001.email))
                    .andExpect(html("#type1").text(vh.getText(Type1Values, inquiryInput03Form_001.type1)))
                    .andExpect(html("#type2").text([Type2Values.ESTIMATE.text
                                                    , Type2Values.CATALOGUE.text
                                                    , Type2Values.OTHER.text].stream().collect(Collectors.joining("、"))))
                    .andExpect(html("#inquiry").text(inquiryInput03Form_001.inquiry))
                    .andExpect(html("#survey > ul > li").count(0))

            and: "確認画面で「送信」ボタンをクリックする"
            mockMvc.perform(post("/inquiry/confirm/send").contentType(MediaType.APPLICATION_FORM_URLENCODED).with(csrf()).session(session))
                    .andExpect(status().isFound())
                    .andExpect(redirectedUrlPattern("**/inquiry/complete/"))

            then: "DBとメールを確認する"
            def rows = sql.rows("SELECT * FROM INQUIRY_DATA")
            rows.size() == 1
            rows[0]["lastname"] == "12345678901234567890"
            rows[0]["firstname"] == "12345678901234567890"
            rows[0]["lastkana"] == "あいうえおかきくけこさしすせそたちつてと"
            rows[0]["firstkana"] == "なにぬねのはひふへほまみむめもあいうえお"
            rows[0]["sex"] == "2"
            rows[0]["age"] == 999
            rows[0]["job"] == StringUtils.EMPTY
            rows[0]["zipcode1"] == "102"
            rows[0]["zipcode2"] == "0072"
            rows[0]["address"] == "東京都千代田区飯田橋1-1"
            rows[0]["tel1"] == StringUtils.EMPTY
            rows[0]["tel2"] == StringUtils.EMPTY
            rows[0]["tel3"] == StringUtils.EMPTY
            rows[0]["email"] == "taro.tanaka@sample.co.jp"
            rows[0]["type1"] == "1"
            rows[0]["type2"] == "1,2,3"
            rows[0]["inquiry"].asciiStream.text == inquiryInput03Form_001.inquiry
            rows[0]["survey"] == StringUtils.EMPTY

            mailServerResource.messagesCount == 1
            MimeMessage message = mailServerResource.firstMessage
            message.subject == "問い合わせフォームからお問い合わせがありました"
            message.content == new ClassPathResource("ksbysample/webapp/bootnpmgeb/web/inquiry/inquirymail-body_002.txt").inputStream.text
        }

    }

}

履歴

2018/07/08
初版発行。