読者です 読者をやめる 読者になる 読者になる

かんがるーさんの日記

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

Spring Boot で書籍の貸出状況確認・貸出申請する Web アプリケーションを作る ( その39 )( 貸出申請画面の作成10 )

概要

Spring Boot で書籍の貸出状況確認・貸出申請する Web アプリケーションを作る ( その38 )( 貸出申請画面の作成9 ) の続きです。

  • 今回の手順で確認できるのは以下の内容です。
    • 貸出申請画面の作成
      • テストの作成 ( 2回目 )
      • ちょっと長くなりすぎたので、全部で3回にします。

参照したサイト・書籍

目次

  1. テスト作成対象クラスの変更
  2. printClassWhatNotMakeTest タスクのチェック対象外のパッケージを設定する
  3. Mail002Helper クラスのテストの作成
  4. UserHelper クラスのテストの作成
  5. LendingappFormValidator クラスのテストの作成
  6. LendingappController クラスのテストの作成

手順

テスト作成対象クラスの変更

  • これまで Controller クラスと Controller クラスから呼び出す Service クラスはテスト内容が重複していたので、Controller クラスのみテストを作成するようにします。LendingappService クラスのテストは作成しません。
  • LendingBookDto はテストするような実装もないのでテスト対象外にします。

printClassWhatNotMakeTest タスクのチェック対象外のパッケージを設定する

クラス名が Dto で終了するクラス全て、及び src/main/java/ksbysample/webapp/lending/web の下のクラス名が Service で終了するクラスをテスト作成対象外にします。

  1. build.gradle を リンク先の内容 に変更します。

  2. 動作確認します。Gradle projects View から printClassWhatNotMakeTest タスクを実行します。

    LendingBookDto、LendingappService が出力されないことが確認できます。

    f:id:ksby:20151228082851p:plain

Mail002Helper クラスのテストの作成

  1. src/main/java/ksbysample/webapp/lending/helper/mail の下の Mail002Helper.java で「Create Test」ダイアログを表示し、テストクラスを作成します。

    f:id:ksby:20151228083217p:plain

  2. src/test/java/ksbysample/webapp/lending/helper/mail の下に Mail002HelperTest.java が作成されますので、リンク先の内容 に変更します。

  3. テストを実行します。Mail002HelperTest クラスのクラス名の左側に表示されているアイコンをクリックしてコンテキストメニューを表示後「Run 'Mail002HelperTest' with Coverage」を選択します。

    テストが成功することが確認できます。

    f:id:ksby:20151228084727p:plain

UserHelper クラスのテストの作成

  1. テストデータを修正し、suzuki hanako に ROLE_APPROVER 権限を付与します。src/test/resources/testdata/base の下の user_role.csvリンク先の内容 に変更します。

  2. src/main/java/ksbysample/webapp/lending/helper/user の下の UserHelper.java で「Create Test」ダイアログを表示し、テストクラスを作成します。

    f:id:ksby:20151229100824p:plain

  3. src/test/java/ksbysample/webapp/lending/helper/user の下に UserHelperTest.java が作成されますので、リンク先の内容 に変更します。

  4. テストを実行します。UserHelperTest クラスのクラス名の左側に表示されているアイコンをクリックしてコンテキストメニューを表示後「Run 'UserHelperTest' with Coverage」を選択します。

    テストが成功することが確認できます。

    f:id:ksby:20151229103001p:plain

LendingappFormValidator クラスのテストの作成

  1. テストデータを作成します。src/test/resources/ksbysample/webapp/lending/web の下に lendingapp ディレクトリを作成します。

  2. src/test/resources/ksbysample/webapp/lending/web/lendingapp の下に LendingappForm_001.yaml, LendingappForm_002.yaml, LendingappForm_003.yaml, LendingappForm_004.yaml を作成し、リンク先の内容 に変更します。

  3. src/main/java/ksbysample/webapp/lending/web/lendingapp の下の LendingappFormValidator.java で「Create Test」ダイアログを表示し、テストクラスを作成します。

    f:id:ksby:20151229104453p:plain

  4. src/test/java/ksbysample/webapp/lending/web/lendingapp の下に LendingappFormValidatorTest.java が作成されますので、リンク先の内容 に変更します。

  5. テストを実行します。LendingappFormValidatorTest クラスのクラス名の左側に表示されているアイコンをクリックしてコンテキストメニューを表示後「Run 'LendingappFormValidatorTest' with Coverage」を選択します。

    テストが成功することが確認できます。

    f:id:ksby:20151229135903p:plain

LendingappController クラスのテストの作成

  1. src/main/java/ksbysample/webapp/lending/web/lendingapp の下の LendingappController.java で「Create Test」ダイアログを表示し、テストクラスを作成します。

    f:id:ksby:20151229141016p:plain

    src/test/java/ksbysample/webapp/lending/web/lendingapp の下に LendingappControllerTest.java が作成されます。

  2. 最初にテストの構成を決めます。src/test/java/ksbysample/webapp/lending/web/lendingapp の下の LendingappControllerTest.javaリンク先のその1の内容 に変更します。

  3. テストデータを作成します。src/test/resources/ksbysample/webapp/lending/web/lendingapp の下に LendingappForm_005.yaml, LendingappForm_006.yaml, LendingappForm_007.yaml を作成し、リンク先の内容 に変更します。

  4. src/test/resources/ksbysample/webapp/lending/web/lendingapp の下に testdata/001 ディレクトリを作成します。

  5. src/test/resources/ksbysample/webapp/lending/web/lendingapp/testdata/001 の下に lending_app, lending_book テーブルの CSV ファイルを作成します。Database tool で以下のデータを lending_app, lending_book テーブルに登録します。

    f:id:ksby:20151229202225p:plain

  6. Database tool で出力する CSV ファイルの設定をします。Database tool で lending_app テーブルを選択後、コンテキストメニューを表示して「Save Data to File」-「Configure CSV Formats...」を選択します。

    f:id:ksby:20151229202828p:plain

  7. CSV Formats」ダイアログが表示されます。以下の操作・設定をした後「OK」ボタンをクリックします。

    • 左上のリストで「Comma Separated Values (CSV)」を選択します。
    • 「Null value text」の入力フィールドに "[null]" を入力します。これによりカラムの値が NULL の場合には "[null]" と出力されるようになります。何気に欲しかった機能で、IDEA の 15 で実装されていました。
    • 「First row is header」のチェックボックスをチェックします。
  8. Database tool で lending_app テーブルを選択後、コンテキストメニューを表示して「Save Data to File」-「Comma Separated Values (CSV)」を選択します。

    f:id:ksby:20151229202440p:plain

  9. 「Save Data To File」ダイアログが表示されます。保存先のディレクトリに src/test/resources/ksbysample/webapp/lending/web/lendingapp/testdata/001 を選択し、ファイル名に "lending_app.csv" を入力した後「OK」ボタンをクリックします。

    f:id:ksby:20151231112614p:plain

  10. 同じ手順で lending_book.csv も作成します。また lending_app.csv, lending_book.csv を出力したディレクトリに test-order.txt も作成します。

  11. lending_app.csv, lending_book.csv, test-order.txt は リンク先の内容 になります。

  12. 検証用のデータを作成します。src/test/resources/ksbysample/webapp/lending/web/lendingapp の下に assertdata/001 ディレクトリを作成します。

  13. src/test/resources/ksbysample/webapp/lending/web/lendingapp/testdata/001 の下の lending_app.csv, lending_book.csv, table-ordering.txt を src/test/resources/ksbysample/webapp/lending/web/lendingapp/assertdata/001 の下へコピーした後、リンク先の内容 に変更します。

  14. src/test/resources/ksbysample/webapp/lending/web/lendingapp/assertdata/001 の下に message.txt を作成します。作成後、リンク先の内容 に変更します。

  15. src/test/resources/ksbysample/webapp/lending/web/lendingapp/assertdata の下に 002 ディレクトリを作成します。

  16. src/test/resources/ksbysample/webapp/lending/web/lendingapp/assertdata/001 の下の lending_app.csv, lending_book.csv, table-ordering.txt を src/test/resources/ksbysample/webapp/lending/web/lendingapp/assertdata/002 の下へコピーした後、リンク先の内容 に変更します。

  17. テスト作成中に気づいた問題点を修正します。

  18. テストを実装します。src/test/java/ksbysample/webapp/lending/web/lendingapp の下の LendingappControllerTest.javaリンク先のその2の内容 に変更します。

  19. テストを実行します。LendingappControllerTest クラスのクラス名の左側に表示されているアイコンをクリックしてコンテキストメニューを表示後「Run 'LendingappControllerTest' with Coverage」を選択します。

    テストが成功することが確認できます。

    f:id:ksby:20151231114600p:plain

ソースコード

build.gradle

task printClassWhatNotMakeTest << {
    def srcDir = new File("src/main/java");
    def excludePaths = [
            "src/main/java/ksbysample/webapp/lending/Application.java"
            , "src/main/java/ksbysample/webapp/lending/config"
            , "src/main/java/ksbysample/webapp/lending/cookie"
            , "src/main/java/ksbysample/webapp/lending/dao"
            , "src/main/java/ksbysample/webapp/lending/entity"
            , "src/main/java/ksbysample/webapp/lending/exception"
            , "src/main/java/ksbysample/webapp/lending/helper/page/PagenationHelper.java"
            , "src/main/java/ksbysample/webapp/lending/security/LendingUser.java"
            , "src/main/java/ksbysample/webapp/lending/security/RoleAwareAuthenticationSuccessHandler.java"
            , "src/main/java/ksbysample/webapp/lending/service/calilapi/response"
            , "src/main/java/ksbysample/webapp/lending/service/file/BooklistCSVRecord.java"
            , "src/main/java/ksbysample/webapp/lending/service/openweathermapapi"
            , "src/main/java/ksbysample/webapp/lending/service/queue/InquiringStatusOfBookQueueMessage.java"
            , "src/main/java/ksbysample/webapp/lending/util/doma"
            , "src/main/java/ksbysample/webapp/lending/util/velocity/VelocityUtils.java"
            , "src/main/java/ksbysample/webapp/lending/web/.+/.+Service.java"
            , "src/main/java/ksbysample/webapp/lending/webapi/common/CommonWebApiResponse.java"
            , "src/main/java/ksbysample/webapp/lending/webapi/weather"
    ];
    def excludeFileNamePatterns = [
            ".*EventListener.java"
            , ".*Dto.java"
            , ".*Form.java"
            , ".*Values.java"
    ];

    compareSrcAndTestDir(srcDir, excludePaths, excludeFileNamePatterns);
}
  • printClassWhatNotMakeTest タスクの excludePaths に以下の値を追加します。
    • , "src/main/java/ksbysample/webapp/lending/web/.+/.+Service.java"
  • printClassWhatNotMakeTest タスクの excludeFileNamePatterns に以下の値を追加します。
    • , ".*Form.java"

Mail002HelperTest.java

package ksbysample.webapp.lending.helper.mail;

import com.google.common.base.Charsets;
import ksbysample.webapp.lending.Application;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;

import javax.mail.Message;
import javax.mail.internet.MimeMessage;
import java.io.File;

import static org.assertj.core.api.Assertions.assertThat;

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = Application.class)
@WebAppConfiguration
public class Mail002HelperTest {

    @Autowired
    private Mail002Helper mail002Helper;

    @Test
    public void testCreateMessage() throws Exception {
        MimeMessage message = mail002Helper.createMessage(new String[]{"test@sample.com", "sample@test.co.jp"}, 1L);
        assertThat(message.getRecipients(Message.RecipientType.TO))
                .extracting(Object::toString)
                .containsOnly("test@sample.com", "sample@test.co.jp");
        assertThat(message.getContent())
                .isEqualTo(com.google.common.io.Files.toString(
                        new File("src/test/resources/ksbysample/webapp/lending/helper/mail/assertdata/002/message.txt")
                        , Charsets.UTF_8));
    }

}

user_role.csv

role_id,user_id,role
1,1,ROLE_USER
2,1,ROLE_ADMIN
3,1,ROLE_APPROVER
4,2,ROLE_USER
5,2,ROLE_APPROVER
6,3,ROLE_USER
7,4,ROLE_USER
8,5,ROLE_USER
  • 5,2,ROLE_APPROVER を追加し、それ以降の role_id を全て +1 します。

UserHelperTest.java

package ksbysample.webapp.lending.helper.user;

import ksbysample.common.test.TestDataResource;
import ksbysample.webapp.lending.Application;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;

import static org.assertj.core.api.Assertions.assertThat;

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = Application.class)
@WebAppConfiguration
public class UserHelperTest {

    @Rule
    @Autowired
    public TestDataResource testDataResource;

    @Autowired
    private UserHelper userHelper;
    
    @Test
    public void testGetApprovalMailAddrList() throws Exception {
        String[] approvalMailAddrList = userHelper.getApprovalMailAddrList();
        assertThat(approvalMailAddrList).containsOnly("tanaka.taro@sample.com", "suzuki.hanako@test.co.jp");
    }
}

LendingappForm_001.yaml, LendingappForm_002.yaml, LendingappForm_003.yaml, LendingappForm_004.yaml, LendingappForm_005.yaml, LendingappForm_006.yaml, LendingappForm_007.yaml

■LendingappForm_001.yaml

!!ksbysample.webapp.lending.web.lendingapp.LendingappForm
# 「一時保存」ボタンが押された場合
# 1件だけ lendingBookDtoList.lendingAppFlg = 1(申請する)、 lendingAppReason = (空) にしている
lendingApp: 
  lendingAppId: 1
  status: 2
  lendingUserId: 1
  approvalUserId: 
  version: 1
btn: temporarySave
lendingBookDtoList: 
  - lendingBookId: 1
    isbn: 978-4-7741-6366-6
    bookName: GitHub実践入門
    lendingState: 蔵書あり
    lendingAppFlg: 1
    lendingAppReason: 
    version: 1
  - lendingBookId: 2
    isbn: 978-4-7741-5377-3
    bookName: JUnit実践入門
    lendingState: 蔵書あり
    lendingAppFlg: 
    lendingAppReason: 
    version: 1
  - lendingBookId: 3
    isbn: 978-4-7973-8014-9
    bookName: Java最強リファレンス
    lendingState: 蔵書あり
    lendingAppFlg: 
    lendingAppReason: 
    version: 1

■LendingappForm_002.yaml

!!ksbysample.webapp.lending.web.lendingapp.LendingappForm
# 1つも「申請する」が選択されていない場合
lendingApp: 
  lendingAppId: 1
  status: 2
  lendingUserId: 1
  approvalUserId: 
  version: 1
btn: apply
lendingBookDtoList: 
  - lendingBookId: 1
    isbn: 978-4-7741-6366-6
    bookName: GitHub実践入門
    lendingState: 蔵書あり
    lendingAppFlg: 
    lendingAppReason: 
    version: 1
  - lendingBookId: 2
    isbn: 978-4-7741-5377-3
    bookName: JUnit実践入門
    lendingState: 蔵書あり
    lendingAppFlg: 
    lendingAppReason: 
    version: 1
  - lendingBookId: 3
    isbn: 978-4-7973-8014-9
    bookName: Java最強リファレンス
    lendingState: 蔵書あり
    lendingAppFlg: 
    lendingAppReason: 
    version: 1

■LendingappForm_003.yaml

!!ksbysample.webapp.lending.web.lendingapp.LendingappForm
# 「申請する」が選択されているが申請理由が入力されていない場合
lendingApp: 
  lendingAppId: 1
  status: 2
  lendingUserId: 1
  approvalUserId: 
  version: 1
btn: apply
lendingBookDtoList: 
  - lendingBookId: 1
    isbn: 978-4-7741-6366-6
    bookName: GitHub実践入門
    lendingState: 蔵書あり
    lendingAppFlg: 1
    lendingAppReason: 
    version: 1
  - lendingBookId: 2
    isbn: 978-4-7741-5377-3
    bookName: JUnit実践入門
    lendingState: 蔵書あり
    lendingAppFlg: 
    lendingAppReason: 
    version: 1
  - lendingBookId: 3
    isbn: 978-4-7973-8014-9
    bookName: Java最強リファレンス
    lendingState: 蔵書あり
    lendingAppFlg: 1
    lendingAppReason: 
    version: 1

■LendingappForm_004.yaml

!!ksbysample.webapp.lending.web.lendingapp.LendingappForm
# 「申請する」が選択されて申請理由も入力されている場合
lendingApp: 
  lendingAppId: 1
  status: 2
  lendingUserId: 1
  approvalUserId: 
  version: 1
btn: apply
lendingBookDtoList: 
  - lendingBookId: 1
    isbn: 978-4-7741-6366-6
    bookName: GitHub実践入門
    lendingState: 蔵書あり
    lendingAppFlg: 1
    lendingAppReason:version: 1
  - lendingBookId: 2
    isbn: 978-4-7741-5377-3
    bookName: JUnit実践入門
    lendingState: 蔵書あり
    lendingAppFlg: 1
    lendingAppReason: 12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678
    version: 1
  - lendingBookId: 3
    isbn: 978-4-7973-8014-9
    bookName: Java最強リファレンス
    lendingState: 蔵書あり
    lendingAppFlg: 
    lendingAppReason: 
    version: 1

■LendingappForm_005.yaml

!!ksbysample.webapp.lending.web.lendingapp.LendingappForm
# lendingBookDtoList.lendingAppFlg のパターンエラー、lendingBookDtoList.lendingAppReason の最大文字数オーバーの場合
lendingApp: 
  lendingAppId: 1
  status: 2
  lendingUserId: 1
  approvalUserId: 
  version: 1
btn: apply
lendingBookDtoList: 
  - lendingBookId: 1
    isbn: 978-4-7741-6366-6
    bookName: GitHub実践入門
    lendingState: 蔵書あり
    lendingAppFlg: 2
    lendingAppReason: パターンエラー
    version: 1
  - lendingBookId: 2
    isbn: 978-4-7741-5377-3
    bookName: JUnit実践入門
    lendingState: 蔵書あり
    lendingAppFlg: 1
    lendingAppReason: 123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789
    version: 1
  - lendingBookId: 3
    isbn: 978-4-7973-8014-9
    bookName: Java最強リファレンス
    lendingState: 蔵書あり
    lendingAppFlg: 
    lendingAppReason: 
    version: 1

■LendingappForm_006.yaml

!!ksbysample.webapp.lending.web.lendingapp.LendingappForm
# 「申請」ボタンクリック時の正常処理の試験用データ
# 「蔵書あり」の4件中3件で「申請する」を選択し、申請理由を入力している
lendingApp: 
  lendingAppId: 105
  status: 2
  lendingUserId: 1
  approvalUserId: 
  version: 1
btn: apply
lendingBookDtoList: 
  - lendingBookId: 521
    isbn: 978-4-7741-6366-6
    bookName: GitHub実践入門
    lendingState: 蔵書なし
    lendingAppFlg: 
    lendingAppReason: 
    version: 1
  - lendingBookId: 522
    isbn: 978-4-7741-5377-3
    bookName: JUnit実践入門
    lendingState: 蔵書あり
    lendingAppFlg: 1
    lendingAppReason: 開発で参照する為
    version: 1
  - lendingBookId: 523
    isbn: 978-4-7973-8014-9
    bookName: Java最強リファレンス
    lendingState: 蔵書あり
    lendingAppFlg: 1
    lendingAppReason: 開発で参照する為
    version: 1
  - lendingBookId: 524
    isbn: 978-4-7973-4778-4
    bookName: アジャイルソフトウェア開発の奥義
    lendingState: 蔵書あり
    lendingAppFlg: 
    lendingAppReason: 
    version: 1
  - lendingBookId: 525
    isbn: 978-4-87311-704-1
    bookName: Javaによる関数型プログラミング
    lendingState: 蔵書あり
    lendingAppFlg: 1
    lendingAppReason: 勉強の為
    version: 1

■LendingappForm_007.yaml

!!ksbysample.webapp.lending.web.lendingapp.LendingappForm
# 「一時保存」ボタンクリック時の正常処理の試験用データ
# 「蔵書あり」の4件中3件で「申請する」を選択し、申請理由を入力している
#   * 1件は「申請する」を選択するが申請理由は入力しない
#   * 1件は「申請する」を選択しないが申請理由は入力する
#   * 1件は「申請する」を選択し、かつ申請理由も入力する
lendingApp: 
  lendingAppId: 105
  status: 2
  lendingUserId: 1
  approvalUserId: 
  version: 1
btn: temporarySave
lendingBookDtoList: 
  - lendingBookId: 521
    isbn: 978-4-7741-6366-6
    bookName: GitHub実践入門
    lendingState: 蔵書なし
    lendingAppFlg: 
    lendingAppReason: 
    version: 1
  - lendingBookId: 522
    isbn: 978-4-7741-5377-3
    bookName: JUnit実践入門
    lendingState: 蔵書あり
    lendingAppFlg: 1
    lendingAppReason: 
    version: 1
  - lendingBookId: 523
    isbn: 978-4-7973-8014-9
    bookName: Java最強リファレンス
    lendingState: 蔵書あり
    lendingAppFlg: 
    lendingAppReason: 検討中
    version: 1
  - lendingBookId: 524
    isbn: 978-4-7973-4778-4
    bookName: アジャイルソフトウェア開発の奥義
    lendingState: 蔵書あり
    lendingAppFlg: 1
    lendingAppReason: 勉強の為
    version: 1
  - lendingBookId: 525
    isbn: 978-4-87311-704-1
    bookName: Javaによる関数型プログラミング
    lendingState: 蔵書あり
    lendingAppFlg: 
    lendingAppReason: 
    version: 1

LendingappFormValidatorTest.java

package ksbysample.webapp.lending.web.lendingapp;

import ksbysample.webapp.lending.Application;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.validation.Errors;
import org.springframework.validation.FieldError;
import org.springframework.validation.MapBindingResult;
import org.yaml.snakeyaml.Yaml;

import java.util.HashMap;

import static org.assertj.core.api.Assertions.assertThat;

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = Application.class)
@WebAppConfiguration
public class LendingappFormValidatorTest {

    // テストデータ
    private LendingappForm lendingappForm_001
            = (LendingappForm) new Yaml().load(getClass().getResourceAsStream("LendingappForm_001.yaml"));
    private LendingappForm lendingappForm_002
            = (LendingappForm) new Yaml().load(getClass().getResourceAsStream("LendingappForm_002.yaml"));
    private LendingappForm lendingappForm_003
            = (LendingappForm) new Yaml().load(getClass().getResourceAsStream("LendingappForm_003.yaml"));
    private LendingappForm lendingappForm_004
            = (LendingappForm) new Yaml().load(getClass().getResourceAsStream("LendingappForm_004.yaml"));

    @Autowired
    private LendingappFormValidator lendingappFormValidator;

    @Test
    public void testValidate_一時保存ボタン押下時は入力チェックは行われない() throws Exception {
        Errors errors = new MapBindingResult(new HashMap<String, String>(), "");
        lendingappFormValidator.validate(lendingappForm_001, errors);
        assertThat(errors.hasFieldErrors()).isFalse();
    }

    @Test
    public void testValidate_1つも申請するが選択されていない場合はエラーになる() throws Exception {
        Errors errors = new MapBindingResult(new HashMap<String, String>(), "");
        lendingappFormValidator.validate(lendingappForm_002, errors);
        assertThat(errors.hasFieldErrors()).isTrue();
        assertThat(errors.getFieldErrorCount()).isEqualTo(3);
        assertThat(errors.getFieldErrors())
                .extracting(FieldError::getField)
                .containsOnly("lendingBookDtoList[0].lendingAppFlg"
                        , "lendingBookDtoList[1].lendingAppFlg"
                        , "lendingBookDtoList[2].lendingAppFlg");
    }

    @Test
    public void testValidate_申請するを選択して申請理由を入力していない場合はエラーになる() throws Exception {
        Errors errors = new MapBindingResult(new HashMap<String, String>(), "");
        lendingappFormValidator.validate(lendingappForm_003, errors);
        assertThat(errors.hasFieldErrors()).isTrue();
        assertThat(errors.getFieldErrorCount()).isEqualTo(2);
        assertThat(errors.getFieldErrors())
                .extracting(FieldError::getField)
                .containsOnly("lendingBookDtoList[0].lendingAppReason"
                        , "lendingBookDtoList[2].lendingAppReason");
    }

    @Test
    public void testValidate_申請するを選択して申請理由も入力している場合はエラーにならない() throws Exception {
        Errors errors = new MapBindingResult(new HashMap<String, String>(), "");
        lendingappFormValidator.validate(lendingappForm_004, errors);
        assertThat(errors.hasFieldErrors()).isFalse();
    }

}

LendingappControllerTest.java

■その1

package ksbysample.webapp.lending.web.lendingapp;

import ksbysample.webapp.lending.Application;
import org.junit.Test;
import org.junit.experimental.runners.Enclosed;
import org.junit.runner.RunWith;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;

@RunWith(Enclosed.class)
public class LendingappControllerTest {

    @RunWith(SpringJUnit4ClassRunner.class)
    @SpringApplicationConfiguration(classes = Application.class)
    @WebAppConfiguration
    public static class 貸出申請画面の初期表示のテスト_エラー処理 {
        
        @Test
        public void ログインしていなければ貸出申請画面は表示できない() throws Exception {
        }

        @Test
        public void lendingAppIdパラメータがなければエラーになる() throws Exception {
        }

        @Test
        public void lendingAppIdパラメータで指定された値が数値でなければエラーになる() throws Exception {
        }

        @Test
        public void lendingAppIdパラメータで指定されたデータが登録されていなければエラーになる() throws Exception {
        }
        
    }

    @RunWith(SpringJUnit4ClassRunner.class)
    @SpringApplicationConfiguration(classes = Application.class)
    @WebAppConfiguration
    public static class 貸出申請画面の初期表示のテスト_正常処理 {

        @Test
        public void lendingAppIdパラメータで指定されたデータが登録されていれば貸出申請画面が表示される() throws Exception {
        }

    }

    @RunWith(SpringJUnit4ClassRunner.class)
    @SpringApplicationConfiguration(classes = Application.class)
    @WebAppConfiguration
    public static class 貸出申請画面の入力チェックエラーのテスト {

        @Test
        public void 申請するが1つも選択されていない場合は入力チェックエラー() throws Exception {
        }

        @Test
        public void 申請するを選択して申請理由を入力していない場合は入力チェックエラー() throws Exception {
        }

        @Test
        public void 最大文字数オーバー_パターンエラーの場合は入力チェックエラー() throws Exception {
        }

    }

    @RunWith(SpringJUnit4ClassRunner.class)
    @SpringApplicationConfiguration(classes = Application.class)
    @WebAppConfiguration
    public static class 貸出申請画面の正常処理時のテスト {

        @Test
        public void 申請ボタンをクリックした場合() throws Exception {
        }

        @Test
        public void 一時保存ボタンをクリックした場合() throws Exception {
        }

    }

}

■その2

package ksbysample.webapp.lending.web.lendingapp;

import com.google.common.base.Charsets;
import ksbysample.common.test.*;
import ksbysample.webapp.lending.Application;
import ksbysample.webapp.lending.helper.message.MessagesPropertiesHelper;
import org.dbunit.dataset.IDataSet;
import org.dbunit.dataset.csv.CsvDataSet;
import org.junit.Rule;
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.SpringApplicationConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MvcResult;
import org.yaml.snakeyaml.Yaml;

import javax.mail.internet.MimeMessage;
import javax.sql.DataSource;
import java.io.File;
import java.util.List;

import static ksbysample.common.test.ErrorsResultMatchers.errors;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;

@RunWith(Enclosed.class)
public class LendingappControllerTest {

    @RunWith(SpringJUnit4ClassRunner.class)
    @SpringApplicationConfiguration(classes = Application.class)
    @WebAppConfiguration
    public static class 貸出申請画面の初期表示のテスト_エラー処理 {

        @Rule
        @Autowired
        public SecurityMockMvcResource mvc;

        @Autowired
        private MessagesPropertiesHelper messagesPropertiesHelper;

        @Test
        public void ログインしていなければ貸出申請画面は表示できない() throws Exception {
            mvc.noauth.perform(get("/lendingapp?lendingAppId=105"))
                    .andExpect(status().isFound())
                    .andExpect(redirectedUrl("http://localhost/"));
        }

        @Test
        public void lendingAppIdパラメータがなければエラーになる() throws Exception {
            MvcResult result = mvc.authTanakaTaro.perform(get("/lendingapp"))
                    .andExpect(status().isOk())
                    .andExpect(content().contentType("text/html;charset=UTF-8"))
                    .andExpect(view().name("error"))
                    .andReturn();
            String content = result.getResponse().getContentAsString();
            assertThat(content).contains(messagesPropertiesHelper.getMessage("LendingappForm.lendingAppId.emptyerr", null));
        }

        @Test
        public void lendingAppIdパラメータで指定された値が数値でなければエラーになる() throws Exception {
            MvcResult result = mvc.authTanakaTaro.perform(get("/lendingapp?lendingAppId=a"))
                    .andExpect(status().isOk())
                    .andExpect(content().contentType("text/html;charset=UTF-8"))
                    .andExpect(view().name("error"))
                    .andReturn();
            String content = result.getResponse().getContentAsString();
            assertThat(content).contains(messagesPropertiesHelper.getMessage("LendingappForm.lendingAppId.emptyerr", null));
        }

        @Test
        public void lendingAppIdパラメータで指定されたデータが登録されていなければエラーになる() throws Exception {
            MvcResult result = mvc.authTanakaTaro.perform(get("/lendingapp?lendingAppId=1"))
                    .andExpect(status().isOk())
                    .andExpect(content().contentType("text/html;charset=UTF-8"))
                    .andExpect(view().name("error"))
                    .andReturn();
            String content = result.getResponse().getContentAsString();
            assertThat(content).contains(messagesPropertiesHelper.getMessage("LendingappForm.lendingApp.nodataerr", null));
        }

    }

    @RunWith(SpringJUnit4ClassRunner.class)
    @SpringApplicationConfiguration(classes = Application.class)
    @WebAppConfiguration
    public static class 貸出申請画面の初期表示のテスト_正常処理 {

        @Rule
        @Autowired
        public TestDataResource testDataResource;

        @Rule
        @Autowired
        public TestDataLoaderResource testDataLoaderResource;
        
        @Rule
        @Autowired
        public SecurityMockMvcResource mvc;

        @Test
        @TestDataLoader("src/test/resources/ksbysample/webapp/lending/web/lendingapp/testdata/001")
        public void lendingAppIdパラメータで指定されたデータが登録されていれば貸出申請画面が表示される() throws Exception {
            MvcResult result = mvc.authTanakaTaro.perform(get("/lendingapp?lendingAppId=105"))
                    .andExpect(status().isOk())
                    .andExpect(content().contentType("text/html;charset=UTF-8"))
                    .andExpect(view().name("lendingapp/lendingapp"))
                    .andExpect(model().hasNoErrors())
                    .andReturn();
            String content = result.getResponse().getContentAsString();
            assertThat(content)
                    .contains("978-4-7741-6366-6").contains("GitHub実践入門")
                    .contains("978-4-7973-8014-9").contains("Java最強リファレンス")
                    .contains("978-4-87311-704-1").contains("Javaによる関数型プログラミング")
                    .contains("978-4-7741-5377-3").contains("JUnit実践入門")
                    .contains("978-4-7973-4778-4").contains("アジャイルソフトウェア開発の奥義");
        }
        
    }

    @RunWith(SpringJUnit4ClassRunner.class)
    @SpringApplicationConfiguration(classes = Application.class)
    @WebAppConfiguration
    public static class 貸出申請画面の入力チェックエラーのテスト {

        // テストデータ
        private LendingappForm lendingappForm_002
                = (LendingappForm) new Yaml().load(getClass().getResourceAsStream("LendingappForm_002.yaml"));
        private LendingappForm lendingappForm_003
                = (LendingappForm) new Yaml().load(getClass().getResourceAsStream("LendingappForm_003.yaml"));
        private LendingappForm lendingappForm_005
                = (LendingappForm) new Yaml().load(getClass().getResourceAsStream("LendingappForm_005.yaml"));

        @Rule
        @Autowired
        public SecurityMockMvcResource mvc;

        @Test
        public void 申請するが1つも選択されていない場合は入力チェックエラー() throws Exception {
            mvc.authTanakaTaro.perform(TestHelper.postForm("/lendingapp/apply", this.lendingappForm_002).with(csrf()))
                    .andExpect(status().isOk())
                    .andExpect(content().contentType("text/html;charset=UTF-8"))
                    .andExpect(view().name("lendingapp/lendingapp"))
                    .andExpect(model().hasErrors())
                    .andExpect(model().errorCount(4))
                    .andExpect(errors().hasGlobalError("lendingappForm", "LendingappForm.lendingBookDtoList.notExistApply"))
                    .andExpect(errors().hasFieldError("lendingappForm", "lendingBookDtoList[0].lendingAppFlg", ""))
                    .andExpect(errors().hasFieldError("lendingappForm", "lendingBookDtoList[1].lendingAppFlg", ""))
                    .andExpect(errors().hasFieldError("lendingappForm", "lendingBookDtoList[2].lendingAppFlg", ""));
        }

        @Test
        public void 申請するを選択して申請理由を入力していない場合は入力チェックエラー() throws Exception {
            mvc.authTanakaTaro.perform(TestHelper.postForm("/lendingapp/apply", this.lendingappForm_003).with(csrf()))
                    .andExpect(status().isOk())
                    .andExpect(content().contentType("text/html;charset=UTF-8"))
                    .andExpect(view().name("lendingapp/lendingapp"))
                    .andExpect(model().hasErrors())
                    .andExpect(model().errorCount(3))
                    .andExpect(errors().hasGlobalError("lendingappForm", "LendingappForm.lendingBookDtoList.emptyReason"))
                    .andExpect(errors().hasFieldError("lendingappForm", "lendingBookDtoList[0].lendingAppReason", ""))
                    .andExpect(errors().hasFieldError("lendingappForm", "lendingBookDtoList[2].lendingAppReason", ""));
        }

        @Test
        public void 最大文字数オーバー_パターンエラーの場合は入力チェックエラー() throws Exception {
            mvc.authTanakaTaro.perform(TestHelper.postForm("/lendingapp/apply", this.lendingappForm_005).with(csrf()))
                    .andExpect(status().isOk())
                    .andExpect(content().contentType("text/html;charset=UTF-8"))
                    .andExpect(view().name("lendingapp/lendingapp"))
                    .andExpect(model().hasErrors())
                    .andExpect(model().errorCount(2))
                    .andExpect(errors().hasFieldError("lendingappForm", "lendingBookDtoList[0].lendingAppFlg", "Pattern"))
                    .andExpect(errors().hasFieldError("lendingappForm", "lendingBookDtoList[1].lendingAppReason", "Size"));
        }

    }

    @RunWith(SpringJUnit4ClassRunner.class)
    @SpringApplicationConfiguration(classes = Application.class)
    @WebAppConfiguration
    public static class 貸出申請画面の正常処理時のテスト {

        // テストデータ
        private LendingappForm lendingappForm_006
                = (LendingappForm) new Yaml().load(getClass().getResourceAsStream("LendingappForm_006.yaml"));
        private LendingappForm lendingappForm_007
                = (LendingappForm) new Yaml().load(getClass().getResourceAsStream("LendingappForm_007.yaml"));

        @Rule
        @Autowired
        public TestDataResource testDataResource;

        @Rule
        @Autowired
        public TestDataLoaderResource testDataLoaderResource;

        @Autowired
        private DataSource dataSource;

        @Rule
        @Autowired
        public MailServerResource mailServerResource;

        @Rule
        @Autowired
        public SecurityMockMvcResource mvc;

        @Test
        @TestDataLoader("src/test/resources/ksbysample/webapp/lending/web/lendingapp/testdata/001")
        public void 申請ボタンをクリックした場合() throws Exception {
            // when ( Spock Framework のブロックの区分けが分かりやすかったので、同じ部分にコメントで付けてみました )
            mvc.authTanakaTaro.perform(TestHelper.postForm("/lendingapp/apply", this.lendingappForm_006).with(csrf()))
                    .andExpect(status().isOk())
                    .andExpect(content().contentType("text/html;charset=UTF-8"))
                    .andExpect(view().name("lendingapp/lendingapp"))
                    .andExpect(model().hasNoErrors());

            // then ( Spock Framework のブロックの区分けが分かりやすかったので、同じ部分にコメントで付けてみました )
            // DB
            IDataSet dataSet = new CsvDataSet(new File("src/test/resources/ksbysample/webapp/lending/web/lendingapp/assertdata/001"));
            TableDataAssert tableDataAssert = new TableDataAssert(dataSet, dataSource);
            tableDataAssert.assertEquals("lending_app", new String[]{"lending_app_id", "approval_user_id"});
            tableDataAssert.assertEquals("lending_book", new String[]{"lending_book_id", "lending_app_id", "lending_state", "lending_app_flg", "lending_app_reason", "approval_result", "approval_reason"});
            // メール ( To にメールアドレスを2つ指定しているためメールが2通送信される )
            assertThat(mailServerResource.getMessagesCount()).isEqualTo(2);
            List<MimeMessage> mimeMessageList = mailServerResource.getMessages();
            for (MimeMessage mimeMessage : mimeMessageList) {
                assertThat(mimeMessage.getRecipients(javax.mail.Message.RecipientType.TO))
                        .extracting(Object::toString)
                        .containsOnly("tanaka.taro@sample.com", "suzuki.hanako@test.co.jp");
                assertThat(mimeMessage.getContent())
                        .isEqualTo(com.google.common.io.Files.toString(
                                new File("src/test/resources/ksbysample/webapp/lending/web/lendingapp/assertdata/001/message.txt")
                                , Charsets.UTF_8));
            }
        }

        @Test
        @TestDataLoader("src/test/resources/ksbysample/webapp/lending/web/lendingapp/testdata/001")
        public void 一時保存ボタンをクリックした場合() throws Exception {
            mvc.authTanakaTaro.perform(TestHelper.postForm("/lendingapp/temporarySave", this.lendingappForm_007).with(csrf()))
                    .andExpect(status().isOk())
                    .andExpect(content().contentType("text/html;charset=UTF-8"))
                    .andExpect(view().name("lendingapp/lendingapp"))
                    .andExpect(model().hasNoErrors());

            // then ( Spock Framework のブロックの区分けが分かりやすかったので、同じ部分にコメントで付けてみました )
            // DB
            IDataSet dataSet = new CsvDataSet(new File("src/test/resources/ksbysample/webapp/lending/web/lendingapp/assertdata/002"));
            TableDataAssert tableDataAssert = new TableDataAssert(dataSet, dataSource);
            tableDataAssert.assertEquals("lending_app", new String[]{"lending_app_id", "approval_user_id"});
            tableDataAssert.assertEquals("lending_book", new String[]{"lending_book_id", "lending_app_id", "lending_state", "lending_app_flg", "lending_app_reason", "approval_result", "approval_reason"});
        }

    }

}

testdata/001/lending_app.csv, lending_book.csv, test-order.txt

■lending_app.csv

lending_app_id,status,lending_user_id,approval_user_id,version
105,2,1,[null],1

■lending_book.csv

lending_book_id,lending_app_id,isbn,book_name,lending_state,lending_app_flg,lending_app_reason,approval_result,approval_reason,version
521,105,978-4-7741-6366-6,GitHub実践入門,蔵書なし,[null],[null],[null],[null],1
523,105,978-4-7973-8014-9,Java最強リファレンス,蔵書あり,[null],[null],[null],[null],1
525,105,978-4-87311-704-1,Javaによる関数型プログラミング,蔵書あり,[null],[null],[null],[null],1
522,105,978-4-7741-5377-3,JUnit実践入門,蔵書あり,[null],[null],[null],[null],1
524,105,978-4-7973-4778-4,アジャイルソフトウェア開発の奥義,蔵書あり,[null],[null],[null],[null],1

■test-order.txt

lending_app
lending_book

assertdata/001/lending_app.csv, lending_book.csv, table-ordering.txt, message.txt

■lending_app.csv

lending_app_id,status,lending_user_id,approval_user_id,version
105,3,1,[null],2

■lending_book.csv

lending_book_id,lending_app_id,isbn,book_name,lending_state,lending_app_flg,lending_app_reason,approval_result,approval_reason,version
521,105,978-4-7741-6366-6,GitHub実践入門,蔵書なし,[null],[null],[null],[null],1
522,105,978-4-7741-5377-3,JUnit実践入門,蔵書あり,1,開発で参照する為,[null],[null],2
523,105,978-4-7973-8014-9,Java最強リファレンス,蔵書あり,1,開発で参照する為,[null],[null],2
524,105,978-4-7973-4778-4,アジャイルソフトウェア開発の奥義,蔵書あり,[null],[null],[null],[null],1
525,105,978-4-87311-704-1,Javaによる関数型プログラミング,蔵書あり,1,勉強の為,[null],[null],2

■table-ordering.txt

lending_app
lending_book

■message.txt

貸出申請がありました。以下のURLから申請内容を確認してください。

http://localhost:8080/lendingapproval?lendingAppId=105

assertdata/002/lending_app.csv, lending_book.csv, table-ordering.txt, message.txt

■lending_app.csv

lending_app_id,status,lending_user_id,approval_user_id,version
105,2,1,[null],1

■lending_book.csv

lending_book_id,lending_app_id,isbn,book_name,lending_state,lending_app_flg,lending_app_reason,approval_result,approval_reason,version
521,105,978-4-7741-6366-6,GitHub実践入門,蔵書なし,[null],[null],[null],[null],2
522,105,978-4-7741-5377-3,JUnit実践入門,蔵書あり,1,[null],[null],[null],2
523,105,978-4-7973-8014-9,Java最強リファレンス,蔵書あり,[null],開発で参照する為,[null],[null],2
524,105,978-4-7973-4778-4,アジャイルソフトウェア開発の奥義,蔵書あり,1,勉強の為,[null],[null],2
525,105,978-4-87311-704-1,Javaによる関数型プログラミング,蔵書あり,[null],[null],[null],[null],2

■table-ordering.txt

lending_app
lending_book

LendingappParamForm.java

package ksbysample.webapp.lending.web.lendingapp;

import lombok.Data;

import javax.validation.constraints.DecimalMax;
import javax.validation.constraints.DecimalMin;
import javax.validation.constraints.NotNull;

@Data
public class LendingappParamForm {

    @NotNull
    @DecimalMin(value = "1")
    @DecimalMax(value = "9223372036854775807")
    private Long lendingAppId;

}

LendingappController.java

    @RequestMapping
    public String index(@Validated LendingappParamForm lendingappParamForm
            , BindingResult bindingResult
            , LendingappForm lendingappForm
            , HttpServletResponse response) {
        if (bindingResult.hasErrors()) {
            if (lendingappParamForm.getLendingAppId() == null) {
                throw new WebApplicationRuntimeException(
                        messagesPropertiesHelper.getMessage("LendingappForm.lendingAppId.emptyerr", null));
            }
            else {
                throw new WebApplicationRuntimeException(
                        messagesPropertiesHelper.getMessage("LendingappForm.lendingApp.nodataerr", null));
            }
        }

        // 画面に表示するデータを取得する
        setDispData(lendingappParamForm.getLendingAppId(), lendingappForm);

        // 未申請の場合には LastLendingAppId Cookie に貸出申請ID をセットする
        if (StringUtils.equals(lendingappForm.getLendingApp().getStatus(), UNAPPLIED.getValue())) {
            CookieUtils.addCookie(CookieLastLendingAppId.class
                    , response, String.valueOf(lendingappParamForm.getLendingAppId()));
        }

        return "lendingapp/lendingapp";
    }
  • index メソッドの引数を BindingResult bindingResultForLendingappParamFormBindingResult bindingResult へ変更します。
  • if (bindingResult.hasErrors()) { ... } 内の処理を lendingAppId パラメータが指定されているか否かで throw するメッセージを変えるようにします。

TestHelper.java

package ksbysample.common.test;

import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;

public class TestHelper {

    /**
     * Formクラスをパラメータに持つMockHttpServletRequestBuilderクラスのインスタンスを生成する
     * 
     * @param urlTemplate
     * @param form
     * @return
     * @throws IllegalAccessException
     */
    public static MockHttpServletRequestBuilder postForm(String urlTemplate, Object form) throws IllegalAccessException {
        MockHttpServletRequestBuilder request = post(urlTemplate).contentType(MediaType.APPLICATION_FORM_URLENCODED);
        setParameterFromForm(request, form, null, null);
        return request;
    }

    /**
     * EntityクラスとFormクラスの値が同じかチェックする
     * 
     * @param entity
     * @param form
     * @throws IllegalAccessException
     */
    public static void assertEntityByForm(Object entity, Object form) throws IllegalAccessException {
        for (Field entityField : entity.getClass().getDeclaredFields()) {
            entityField.setAccessible(true);
            try {
                Field formField = form.getClass().getDeclaredField(entityField.getName());
                formField.setAccessible(true);
                assertThat(entityField.get(entity), is(formField.get(form)));
            }
            catch (NoSuchFieldException ignored) {}
        }
    }

    /**
     * Form クラスのインスタンスのフィールド名と値を request にセットする
     *
     * @param request
     * @param form
     * @throws IllegalAccessException
     */
    private static void setParameterFromForm(MockHttpServletRequestBuilder request, Object form, String fieldName, Integer arrayIndex) throws IllegalAccessException {
        for (Field field : form.getClass().getDeclaredFields()) {
            field.setAccessible(true);
            if (field.get(form) == null) {
                request = request.param(makeParameterName(fieldName, field.getName(), arrayIndex), "");
            }
            else if (field.get(form) instanceof ArrayList<?>) {
                Integer i = 0;
                for (Object obj : (List<?>)field.get(form)) {
                    if (isPrimitiveOrString(obj)) {
                        request = request.param(makeParameterName(fieldName, field.getName(), i), obj.toString());
                    }
                    else {
                        setParameterFromForm(request, obj, makeParameterName(fieldName, field.getName(), arrayIndex), i);
                    }
                    i++;
                }
            }
            else if (isPrimitiveOrString(field.get(form))) {
                request = request.param(makeParameterName(fieldName, field.getName(), arrayIndex), field.get(form).toString());
            }
            else {
                setParameterFromForm(request, field.get(form), makeParameterName(fieldName, field.getName(), arrayIndex), null);
            }
        }
    }

    /**
     * 指定されたオブジェクトがプリミティブ型かチェックする
     *
     * @param obj
     * @return
     */
    private static boolean isPrimitiveOrString(Object obj) {
        boolean result = false;
        if (obj instanceof Byte) result = true;
        else if (obj instanceof Short) result = true;
        else if (obj instanceof Integer) result = true;
        else if (obj instanceof Long) result = true;
        else if (obj instanceof Character) result = true;
        else if (obj instanceof Float) result = true;
        else if (obj instanceof Double) result = true;
        else if (obj instanceof Boolean) result = true;
        else if (obj instanceof String) result = true;
        return result;
    }

    /**
     * MockHttpServletRequestBuilderクラスのインスタンス ( request ) にセットするパラメータ名を生成する
     *
     * @param rootFieldName
     * @param fieldName
     * @param arrayIndex
     * @return
     */
    private static String makeParameterName(String rootFieldName, String fieldName, Integer arrayIndex) {
        StringBuilder sb = new StringBuilder();
        if (rootFieldName != null) {
            sb.append(rootFieldName);
            if (arrayIndex != null) {
                sb.append("[");
                sb.append(arrayIndex);
                sb.append("]");
            }
            sb.append(".");
        }
        sb.append(fieldName);
        return sb.toString();
    }
    
}
  • Form クラス内に String あるいは List<String> 以外の型のフィールドが存在した場合に postForm メソッドで処理できない問題を解消するために以下の修正を行います。

履歴

2015/12/31
初版発行。