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

かんがるーさんの日記

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

Spring Boot でメール送信する Web アプリケーションを作る ( その19 )( 送信済メール検索画面の作成2 )

概要

Spring Boot でメール送信する Web アプリケーションを作る ( その18 )( 送信済メール検索画面の作成 ) の続きです。

  • 今回の手順で確認できるのは以下の内容です。
    • 送信済メール検索画面の作成
    • 今回はページネーションを実装します。

ソフトウェア一覧

参考にしたサイト

  1. Doma 2 - User Documentation - クエリ - 検索 - 検索オプションを利用した検索
    http://doma.readthedocs.org/ja/latest/query/select/#id10

手順

ブランチの作成

  1. IntelliJ IDEA で 1.0.x-mailsearch-pagenation ブランチを作成します。

ページネーションの実装仕様

以下の仕様で実装します。

SQL ファイルの修正

  1. 値が null でない時だけでなく空でない時にも where 句に条件文を出力しないようにしたいので、SQL ファイルを修正します。src/main/resources/META-INF/ksbysample/webapp/email/web/mailsearch/MailsearchDao の下の selectCondition.sqlリンク先の内容 に変更します。

MailsearchDao インターフェースの修正

  1. src/main/java/ksbysample/webapp/email/web/mailsearch の下の MailsearchDao.javaリンク先の内容 に変更します。

  2. src/test/java/ksbysample/webapp/email/web/mailsearch の下の MailsearchDaoTest.javaリンク先の内容 に変更します。

  3. テストを実行します。testSelectConditionOptions テストメソッドコンテキストメニューを表示し、「Run 'testSelectConditionO...' with Coverage」を選択し、テストが成功することを確認します。

MailsearchService クラスの作成

  1. src/main/java/ksbysample/webapp/email/web/mailsearch の下に MailsearchService.java を新規作成します。作成後、リンク先の内容 に変更します。

MailsearchController クラス、Thymeleaf テンプレートファイルの修正

  1. ksbysample-webapp-basic から以下のファイルをコピーします。

    • src/main/java/ksbysample/webapp/basic/helper/PagenationHelper.java
      → src/main/java/ksbysample/webapp/email/helper/PagenationHelper.java
  2. src/main/java/ksbysample/webapp/email/web/mailsearch の下の MailsearchController.javaリンク先の内容 に変更します。

  3. src/main/resources/templates/common の下に pagenation.html を新規作成します。作成後、リンク先の内容 に変更します。

  4. src/main/resources/templates/mailsearch の下の mailsearch.html を リンク先の内容 に変更します。

動作確認

  1. Gradle projects View から bootRun タスクを実行して Tomcat を起動します。

  2. ブラウザを起動し http://localhost:8080/mailsearch へアクセスします。今回はページネーションが機能していることを確認します。

    f:id:ksby:20150616061534p:plain

    1件しかヒットしない場合は以下の画像のようになります。

    f:id:ksby:20150616062355p:plain

  3. Run View で Ctrl+F2 を押して Tomcat を停止します。

MailsearchControllerTest クラスの作成

  1. ksbysample-webapp-basic から以下のファイルをコピーします。

    • src/test/java/ksbysample/webapp/basic/test/CustomModelResultMatchers.java
      → src/test/java/ksbysample/webapp/email/test/CustomModelResultMatchers.java
  2. src/test/resources/ksbysample/webapp/email/web/mailsearch の下に mailsearchForm_empty.yml, mailsearchForm_toAddr.yml, mailsearchForm_subject.yml, mailsearchForm_name.yml, mailsearchForm_type.yml を新規作成します。作成後、リンク先の内容 に変更します。

  3. src/test/java/ksbysample/webapp/email/web/mailsearch の下に MailsearchControllerTest.java を新規作成します。作成後、リンク先の内容 に変更します。

  4. テストを実行します。MailsearchControllerTest テストメソッドコンテキストメニューを表示し、「Run 'MailsearchController...' with Coverage」を選択し、テストが成功することを確認します。

    f:id:ksby:20150617002436p:plain

commit、Push、Pull Request、マージ

  1. Project View のルートでコンテキストメニューを表示して「Run 'Tests in 'ksbysample...' with Coverage」を選択し、テストが全て成功することを確認します。

  2. Gradle projects View から build タスクを実行し、"BUILD SUCCESSFUL" が出力されることを確認します。

  3. commit します。

  4. GitHub へ Push、1.0.x-mailsearch-pagenation -> 1.0.x へ Pull Request、1.0.x でマージ、1.0.x-mailsearch-pagenation ブランチを削除、をします。

次回は。。。

いくつか気になっている点があるので、その辺を修正します。

  • Doma が実行された SQL 文をログに出力しますが、log4jdbc-log4j2 で SQL ファイルを出力するので Doma の方では出力しないようにします。
  • Tomcat 起動中に SQL ファイルを修正しても反映されませんでした ( JRebel で起動していてもダメでした )。反映されるようにする方法があるか調査します。

ソースコード

selectCondition.sql

select
  /*%expand "em" */*
from
  email em
where
  /*%if mailsearchForm.toAddr != null && mailsearchForm.toAddr != "" */
  em.to_addr like /* @infix(mailsearchForm.toAddr) */'%t%'
  /*%end*/
  /*%if mailsearchForm.subject != null && mailsearchForm.subject != "" */
  and em.subject like /* @infix(mailsearchForm.subject) */'%スト%'
  /*%end*/
  /*%if mailsearchForm.name != null && mailsearchForm.name != "" */
  and em.name like /* @infix(mailsearchForm.name) */'%花%'
  /*%end*/
  /*%if mailsearchForm.type != null && mailsearchForm.type.size() > 0 */
  and em.type in /* mailsearchForm.type */('1', '3')
  /*%end*/
order by em.to_addr
  • where 句の if 文に && mailsearchForm.[Formクラスのフィールド名] != "" の条件を追加します。type だけは String 型でなく List 型のため && mailsearchForm.type.size() > 0 にしています。

MailsearchDao.java

package ksbysample.webapp.email.web.mailsearch;

import ksbysample.webapp.email.annotation.dao.ComponentAndAutowiredDomaConfig;
import ksbysample.webapp.email.entity.Email;
import org.seasar.doma.Dao;
import org.seasar.doma.Select;
import org.seasar.doma.jdbc.SelectOptions;

import java.util.List;

@Dao
@ComponentAndAutowiredDomaConfig
public interface MailsearchDao {

    @Select
    List<Email> selectCondition(MailsearchForm mailsearchForm);

    @Select
    List<Email> selectCondition(MailsearchForm mailsearchForm, SelectOptions options);

}
  • List<Email> selectCondition(MailsearchForm mailsearchForm, SelectOptions options); を追加します。

MailsearchDaoTest.java

package ksbysample.webapp.email.web.mailsearch;

import ksbysample.webapp.email.Application;
import ksbysample.webapp.email.entity.Email;
import ksbysample.webapp.email.test.TestDataLoader;
import ksbysample.webapp.email.test.TestDataLoaderResource;
import ksbysample.webapp.email.test.TestDataResource;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.seasar.doma.Select;
import org.seasar.doma.jdbc.SelectOptions;
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 java.util.Arrays;
import java.util.List;

import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;

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

    @Rule
    @Autowired
    public TestDataResource testDataResource;

    @Rule
    @Autowired
    public TestDataLoaderResource testDataLoaderResource;

    @Autowired
    private MailsearchDao mailsearchDao;

    @Test
    @TestDataLoader("src/test/resources/ksbysample/webapp/email/web/mailsearch/testdata")
    public void testSelectCondition() throws Exception {
        MailsearchForm mailsearchForm = new MailsearchForm();
        List<Email> emailList = mailsearchDao.selectCondition(mailsearchForm);
        // emailList.stream().forEach(s -> System.out.println(s.getToAddr()));
        assertThat(emailList.size(), is(5));

        mailsearchForm = new MailsearchForm();
        mailsearchForm.setToAddr("t");
        mailsearchForm.setSubject("スト");
        mailsearchForm.setName("花");
        mailsearchForm.setType(Arrays.asList("3"));
        emailList = mailsearchDao.selectCondition(mailsearchForm);
        assertThat(emailList.size(), is(1));

        mailsearchForm = new MailsearchForm();
        mailsearchForm.setName("太郎");
        emailList = mailsearchDao.selectCondition(mailsearchForm);
        assertThat(emailList.size(), is(3));
    }

    @Test
    @TestDataLoader("src/test/resources/ksbysample/webapp/email/web/mailsearch/testdata")
    public void testSelectConditionOptions() throws Exception {
        MailsearchForm mailsearchForm = new MailsearchForm();
        List<Email> emailList = mailsearchDao.selectCondition(mailsearchForm);
        assertThat(emailList.size(), is(5));
        assertThat(emailList.get(0).getToAddr(), is("hanako@sample.com"));

        // offset(0).limit(1) を指定すると最初の1件目だけ取得できる
        mailsearchForm = new MailsearchForm();
        SelectOptions options = SelectOptions.get().offset(0).limit(1).count();
        emailList = mailsearchDao.selectCondition(mailsearchForm, options);
        assertThat(emailList.size(), is(1));
        assertThat(emailList.get(0).getToAddr(), is("hanako@sample.com"));
        // options.getCount() で offset, limit が指定されていない時の全件数を取得できる
        long count = options.getCount();
        assertThat(count, is(5L));
    }

}
  • testSelectConditionOptions テストメソッドを追加します。

MailsearchService.java

package ksbysample.webapp.email.web.mailsearch;

import ksbysample.webapp.email.entity.Email;
import org.seasar.doma.jdbc.SelectOptions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class MailsearchService {

    @Autowired
    private MailsearchDao mailsearchDao;
    
    public Page<Email> searchEmail(MailsearchForm mailsearchForm, Pageable pageable) {
        int offset = pageable.getPageNumber() * pageable.getPageSize();
        int limit = pageable.getPageSize();
        SelectOptions options = SelectOptions.get().offset(offset).limit(limit).count();

        List<Email> emailList = mailsearchDao.selectCondition(mailsearchForm, options);
        long count = options.getCount();

        Page<Email> page = new PageImpl<Email>(emailList, pageable, count);
        return page;
    }

}

MailsearchController.java

package ksbysample.webapp.email.web.mailsearch;

import ksbysample.webapp.email.entity.Email;
import ksbysample.webapp.email.helper.PagenationHelper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.web.PageableDefault;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
@RequestMapping("/mailsearch")
public class MailsearchController {

    private static final int DEFAULT_PAGEABLE_SIZE = 2;

    @Autowired
    private MailsearchService mailsearchService;

    @RequestMapping
    public String index(MailsearchForm mailsearchForm
            , @PageableDefault(size = DEFAULT_PAGEABLE_SIZE, page = 0) Pageable pageable
            , Model model) {
        Page<Email> page = mailsearchService.searchEmail(mailsearchForm, pageable);
        PagenationHelper ph = new PagenationHelper(page);

        model.addAttribute("page", page);
        model.addAttribute("ph", ph);

        return "mailsearch/mailsearch";
    }

}
  • private static final int DEFAULT_PAGEABLE_SIZE = 2; を追加します。ページネーションの動作を確認しやすくするため、1ページの最大表示件数を2件に設定します。
  • private MailsearchDao mailsearchDao;private MailsearchService mailsearchService; へ変更します。
  • index メソッドを以下のように変更します。
    • 引数に @PageableDefault(size = DEFAULT_PAGEABLE_SIZE, page = 0) Pageable pageable を追加します。
    • データ一覧は mailsearchService.searchEmail(...) を呼び出して取得します。
    • Thymeleaf テンプレートファイルでページネーションの処理を行うために PagenationHelper クラスのインスタンスを生成します。
    • model に page, ph をセットします。

pagenation.html

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="utf-8"/>
    <meta http-equiv="X-UA-Compatible" content="IE=edge"/>
    <meta name="viewport" content="width=device-width, initial-scale=1"/>

    <title>xxxxxxxx</title>

    <!-- Bootstrap core CSS -->
    <link href="/css/bootstrap.min.css" rel="stylesheet"/>

    <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
    <!--[if lt IE 9]>
    <script src="/js/html5shiv.min.js"></script>
    <script src="/js/respond.min.js"></script>
    <![endif]-->

    <!-- Custom styles for this template -->
    <style>
    <!--
    body {
        padding-top: 50px;
    }
    .navbar-brand {
        font-size: 24px;
    }
    .form-group {
        margin-bottom: 5px;
    }
    -->
    </style>
</head>

<body>

    <div th:fragment="pagenation (url, page, ph)">
        <div class="row">
            <div class="col-xs-12">
                <div class="dataTables_paginate paging_bootstrap">
                    <ul class="pagination">
                        <li class="prev" th:classappend="${ph.hiddenPrev} ? 'disabled'">
                            <a class="js-pagenation" th:href="@{${url}(page=${page.number - 1},size=${page.size})}">← Previous</a>
                        </li>
                        <li th:class="${ph.activePage1} ? 'active'">
                            <a class="js-pagenation" th:href="@{${url}(page=${ph.page1PageValue},size=${page.size})}" th:text="${ph.page1PageValue + 1}">1</a>
                        </li>
                        <li th:class="${ph.activePage2} ? 'active'" th:classappend="${ph.hiddenPage2} ? 'disabled'">
                            <a class="js-pagenation" th:href="@{${url}(page=${ph.page1PageValue + 1},size=${page.size})}" th:text="${ph.page1PageValue + 2}">2</a>
                        </li>
                        <li th:class="${ph.activePage3} ? 'active'" th:classappend="${ph.hiddenPage3} ? 'disabled'">
                            <a class="js-pagenation" th:href="@{${url}(page=${ph.page1PageValue + 2},size=${page.size})}" th:text="${ph.page1PageValue + 3}">3</a>
                        </li>
                        <li th:class="${ph.activePage4} ? 'active'" th:classappend="${ph.hiddenPage4} ? 'disabled'">
                            <a class="js-pagenation" th:href="@{${url}(page=${ph.page1PageValue + 3},size=${page.size})}" th:text="${ph.page1PageValue + 4}">4</a>
                        </li>
                        <li th:class="${ph.activePage5} ? 'active'" th:classappend="${ph.hiddenPage5} ? 'disabled'">
                            <a class="js-pagenation" th:href="@{${url}(page=${ph.page1PageValue + 4},size=${page.size})}" th:text="${ph.page1PageValue + 5}">5</a>
                        </li>
                        <li class="next" th:classappend="${ph.hiddenNext} ? 'disabled'">
                            <a class="js-pagenation" th:href="@{${url}(page=${page.number + 1},size=${page.size})}">Next → </a>
                        </li>
                    </ul>
                </div>
            </div>
        </div>
    </div>

</body>
</html>

mailsearch.html

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8"/>
    <title>ksbysample-webapp-email</title>
    <meta content='width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no' name='viewport'/>

    <meta th:replace="common/head-cssjs"/>
    <style>
    <!--
    .form-group {
        margin-bottom: 5px;
    }
    .table {
        margin-top: 10px;
        margin-bottom: 0px;
    }
    .table>tbody>tr>td
    , .table>tbody>tr>th
    , .table>tfoot>tr>td
    , .table>tfoot>tr>th
    , .table>thead>tr>td
    , .table>thead>tr>th {
        padding: 5px;
    }
    .checkbox label,
    .radio label {
        padding-right: 10px;
    }
    .pagination > .disabled > a
    , .pagination > .disabled > a:focus
    , .pagination > .disabled > a:hover
    , .pagination > .disabled > span
    , .pagination > .disabled > span:focus
    , .pagination > .disabled > span:hover {
        color: #eee;
        cursor: not-allowed;
        background-color: #fff;
        border-color: #ddd;
    }
    -->
    </style>
</head>
<body class="skin-blue">
<div class="wrapper">

    <!-- Main Header -->
    <div th:replace="common/mainparts :: main-header"></div>

    <!-- Left side column. contains the logo and sidebar -->
    <div th:replace="common/mainparts :: main-sidebar (active='mailsearch')"></div>

    <!-- Content Wrapper. Contains page content -->
    <div class="content-wrapper">
        <!-- Content Header (Page header) -->
        <section class="content-header">
            <h1>
                送信済メール検索画面
            </h1>
        </section>

        <!-- Main content -->
        <section class="content">
            <div class="row">
                <div class="col-xs-12">
                    <div class="box">
                        <div class="box-header with-border bg-purple-gradient">
                            <div class="row">
                                <div class="col-xs-12">
                                    <form id="mailSearchForm" method="post" action="/mailsearch" th:action="@{/mailsearch}" th:object="${mailsearchForm}" class="form-horizontal">
                                        <div class="form-group">
                                            <label for="toAddr" class="control-label col-sm-2">To</label>
                                            <div class="col-sm-10">
                                                <div class="row"><div class="col-sm-8"><div class="input-group"><span class="input-group-addon"><i class="fa fa-envelope"></i></span><input type="text" name="toAddr" id="toAddr" class="form-control input-sm" value="" placeholder="" th:field="*{toAddr}"/></div></div></div>
                                            </div>
                                        </div>

                                        <div class="form-group">
                                            <label for="subject" class="control-label col-sm-2">Subject</label>
                                            <div class="col-sm-10">
                                                <div class="row"><div class="col-sm-12"><input type="text" name="subject" id="subject" class="form-control input-sm" value="" placeholder="" th:field="*{subject}"/></div></div>
                                            </div>
                                        </div>

                                        <div class="form-group">
                                            <label for="name" class="control-label col-sm-2">氏名</label>
                                            <div class="col-sm-10">
                                                <div class="row"><div class="col-sm-8"><input type="text" name="name" id="name" class="form-control input-sm" value="" placeholder="" th:field="*{name}"/></div></div>
                                            </div>
                                        </div>

                                        <div class="form-group">
                                            <label class="control-label col-sm-2">項目</label>
                                            <div class="col-sm-10">
                                                <div class="row"><div class="col-sm-12">
                                                    <div class="checkbox">
                                                        <label th:each="type : ${T(ksbysample.webapp.email.config.Constant).getInstance().TYPE_MAP.entrySet()}">
                                                            <input type="checkbox" name="type" th:value="${type.getKey()}" th:text="${type.getValue()}" th:field="*{type}"/>
                                                        </label>
                                                    </div>
                                                </div></div>
                                            </div>
                                        </div>
                                        <div class="text-center">
                                            <button type="button" id="search" value="search" class="btn btn-primary bg-gray">検索</button>
                                        </div>
                                    </form>
                                </div>
                            </div>
                        </div>
                        <div class="box-body">
                            <div id="maillist_wrapper" class="dataTables_wrapper form-inline" role="grid">
                                <table id="maillist" class="table table-bordered table-hover dataTable" aria-describedby="maillist_info">
                                    <thead class="bg-purple">
                                        <tr role="row">
                                            <th role="columnheader" tabindex="0" aria-controls="maillist" rowspan="1" colspan="1">To</th>
                                            <th role="columnheader" tabindex="0" aria-controls="maillist" rowspan="1" colspan="1">Subject</th>
                                            <th role="columnheader" tabindex="0" aria-controls="maillist" rowspan="1" colspan="1">氏名</th>
                                            <th role="columnheader" tabindex="0" aria-controls="maillist" rowspan="1" colspan="1">項目</th>
                                        </tr>
                                    </thead>
                                    <tbody role="alert" aria-live="polite" aria-relevant="all">
                                        <tr th:each="email : ${page.content}">
                                            <td th:text="${email.toAddr}">test@sample.com</td>
                                            <td th:text="${email.subject}">件名1</td>
                                            <td th:text="${email.name}">田中 太郎</td>
                                            <td th:text="${T(ksbysample.webapp.email.config.Constant).getInstance().TYPE_MAP.get(email.type)}">資料請求</td>
                                        </tr>
                                    </tbody>
                                </table>

                                <div id="pagenation" th:include="common/pagenation :: pagenation (url='/mailsearch', page=${page}, ph=${ph})"></div>

                                <form id="pagenationForm" method="post" action="#" th:action="@{#}" th:object="${mailsearchForm}">
                                    <input type="hidden" name="toAddr" id="toAddr" th:value="*{toAddr}"/>
                                    <input type="hidden" name="subject" id="subject" th:value="*{subject}"/>
                                    <input type="hidden" name="name" id="name" th:value="*{name}"/>
                                    <input type="hidden" name="type" id="type" th:each="typeItem : *{type}" th:value="${typeItem}"/>
                                </form>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </section>
        <!-- /.content -->
    </div>
    <!-- /.content-wrapper -->

</div>
<!-- ./wrapper -->

<!-- REQUIRED JS SCRIPTS -->

<div th:replace="common/bottom-js"></div>
<script type="text/javascript">
<!--
$(document).ready(function() {
    $('#toAddr').focus();

    $('#search').bind('click', function(){
        $('#mailSearchForm').submit();
    });

    $('.js-pagenation').each(function(){
        $(this).click(function(){
            $('#pagenationForm').attr('action', $(this).attr('href'));
            $(this).attr('href', '#');
            $('#pagenationForm').submit();
        });
    });
});
-->
</script>
</body>
</html>
  • ページネーションで押せない部分の文字の色を薄くしたいので、CSS.pagination > .disabled > a, , ... { ... } を追加します。
  • ヒットしたデータを表示する部分で tbody タグが複数出力されていることに気づきました。tbody タグに入れていた th:each="email : ${page.content}" をその下の tr タグへ移動します。
  • ページネーションの HTML の部分を <div id="pagenation" th:include="common/pagenation :: pagenation (url='/mailsearch', page=${page}, ph=${ph})"></div> へ変更します。
  • ページネーション処理用のフォーム <form id="pagenationForm" ...> ... </form> を追加します。
  • $('.js-pagenation').each(function(){ ... } を追加します。

mailsearchForm_empty.yml, mailsearchForm_toAddr.yml, mailsearchForm_subject.yml, mailsearchForm_name.yml, mailsearchForm_type.yml

■mailsearchForm_empty.yml

!!ksbysample.webapp.email.web.mailsearch.MailsearchForm
toAddr: 
subject: 
name: 

■mailsearchForm_toAddr.yml

!!ksbysample.webapp.email.web.mailsearch.MailsearchForm
toAddr: yyy
subject: 
name: 

■mailsearchForm_subject.yml

!!ksbysample.webapp.email.web.mailsearch.MailsearchForm
toAddr: 
subject: スト
name: 

■mailsearchForm_name.yml

!!ksbysample.webapp.email.web.mailsearch.MailsearchForm
toAddr: 
subject: 
name: 鈴木

■mailsearchForm_type.yml

!!ksbysample.webapp.email.web.mailsearch.MailsearchForm
toAddr: 
subject: 
name: 
type: 
  - 2
  - 3

MailsearchControllerTest.java

package ksbysample.webapp.email.web.mailsearch;

import ksbysample.webapp.email.Application;
import ksbysample.webapp.email.test.*;
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 org.yaml.snakeyaml.Yaml;

import static ksbysample.webapp.email.test.CustomModelResultMatchers.modelEx;
import static org.hamcrest.Matchers.is;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;

@RunWith(Enclosed.class)
public class MailsearchControllerTest {

    @RunWith(SpringJUnit4ClassRunner.class)
    @SpringApplicationConfiguration(classes = Application.class)
    @WebAppConfiguration
    public static class 初期表示のテスト {

        @Rule
        @Autowired
        public MockMvcResource mvc;

        @Test
        public void 送信済メール検索画面を表示する() throws Exception {
            // 送信済メール検索画面が表示されることを確認する
            mvc.nonauth.perform(get("/mailsearch"))
                    .andExpect(status().isOk())
                    .andExpect(content().contentType("text/html;charset=UTF-8"))
                    .andExpect(view().name("mailsearch/mailsearch"))
                    .andExpect(modelEx().property("page.number", is(0)));
        }

    }

    @RunWith(SpringJUnit4ClassRunner.class)
    @SpringApplicationConfiguration(classes = Application.class)
    @WebAppConfiguration
    public static class 検索条件は何も入力しないで検索する場合 {

        @Rule
        @Autowired
        public TestDataResource testDataResource;

        @Rule
        @Autowired
        public TestDataLoaderResource testDataLoaderResource;

        @Rule
        @Autowired
        public MockMvcResource mvc;

        // テストデータ
        private MailsearchForm mailsearchFormEmpty
                = (MailsearchForm) new Yaml().load(getClass().getResourceAsStream("mailsearchForm_empty.yml"));

        @Test
        @TestDataLoader("src/test/resources/ksbysample/webapp/email/web/mailsearch/testdata")
        public void 検索ボタンをクリックすると1ページ目が表示される() throws Exception {
            mvc.nonauth.perform(TestHelper.postForm("/mailsearch", this.mailsearchFormEmpty))
                    .andExpect(status().isOk())
                    .andExpect(content().contentType("text/html;charset=UTF-8"))
                    .andExpect(view().name("mailsearch/mailsearch"))
                    .andExpect(modelEx().property("page.number", is(0)))
                    .andExpect(modelEx().property("page.size", is(2)))
                    .andExpect(modelEx().property("page.totalPages", is(3)))
                    .andExpect(modelEx().property("ph.page1PageValue", is(0)))
                    .andExpect(modelEx().property("ph.hiddenPrev", is(true)))
                    .andExpect(modelEx().property("ph.hiddenNext", is(false)));
        }

        @Test
        @TestDataLoader("src/test/resources/ksbysample/webapp/email/web/mailsearch/testdata")
        public void 検索条件はそのままで2ページ目へ() throws Exception {
            mvc.nonauth.perform(TestHelper.postForm("/mailsearch?page=1&size=2", this.mailsearchFormEmpty))
                    .andExpect(status().isOk())
                    .andExpect(content().contentType("text/html;charset=UTF-8"))
                    .andExpect(view().name("mailsearch/mailsearch"))
                    .andExpect(modelEx().property("page.number", is(1)))
                    .andExpect(modelEx().property("page.size", is(2)))
                    .andExpect(modelEx().property("page.totalPages", is(3)))
                    .andExpect(modelEx().property("ph.page1PageValue", is(0)))
                    .andExpect(modelEx().property("ph.hiddenPrev", is(false)))
                    .andExpect(modelEx().property("ph.hiddenNext", is(false)));
        }
        
    }

    @RunWith(SpringJUnit4ClassRunner.class)
    @SpringApplicationConfiguration(classes = Application.class)
    @WebAppConfiguration
    public static class 検索条件を入力して検索する場合 {

        @Rule
        @Autowired
        public TestDataResource testDataResource;

        @Rule
        @Autowired
        public TestDataLoaderResource testDataLoaderResource;

        @Rule
        @Autowired
        public MockMvcResource mvc;

        // テストデータ
        private MailsearchForm mailsearchFormToAddr
                = (MailsearchForm) new Yaml().load(getClass().getResourceAsStream("mailsearchForm_toAddr.yml"));
        private MailsearchForm mailsearchFormSubject
                = (MailsearchForm) new Yaml().load(getClass().getResourceAsStream("mailsearchForm_subject.yml"));
        private MailsearchForm mailsearchFormName
                = (MailsearchForm) new Yaml().load(getClass().getResourceAsStream("mailsearchForm_name.yml"));
        private MailsearchForm mailsearchFormType
                = (MailsearchForm) new Yaml().load(getClass().getResourceAsStream("mailsearchForm_type.yml"));

        @Test
        @TestDataLoader("src/test/resources/ksbysample/webapp/email/web/mailsearch/testdata")
        public void toAddrのみで検索して3件ヒットする場合() throws Exception {
            mvc.nonauth.perform(TestHelper.postForm("/mailsearch", this.mailsearchFormToAddr))
                    .andExpect(status().isOk())
                    .andExpect(content().contentType("text/html;charset=UTF-8"))
                    .andExpect(view().name("mailsearch/mailsearch"))
                    .andExpect(modelEx().property("page.number", is(0)))
                    .andExpect(modelEx().property("page.size", is(2)))
                    .andExpect(modelEx().property("page.totalPages", is(2)))
                    .andExpect(modelEx().property("ph.page1PageValue", is(0)))
                    .andExpect(modelEx().property("ph.hiddenPrev", is(true)))
                    .andExpect(modelEx().property("ph.hiddenNext", is(false)));
        }

        @Test
        @TestDataLoader("src/test/resources/ksbysample/webapp/email/web/mailsearch/testdata")
        public void subjectのみで検索して5件ヒットする場合() throws Exception {
            mvc.nonauth.perform(TestHelper.postForm("/mailsearch", this.mailsearchFormSubject))
                    .andExpect(status().isOk())
                    .andExpect(content().contentType("text/html;charset=UTF-8"))
                    .andExpect(view().name("mailsearch/mailsearch"))
                    .andExpect(modelEx().property("page.number", is(0)))
                    .andExpect(modelEx().property("page.size", is(2)))
                    .andExpect(modelEx().property("page.totalPages", is(3)))
                    .andExpect(modelEx().property("ph.page1PageValue", is(0)))
                    .andExpect(modelEx().property("ph.hiddenPrev", is(true)))
                    .andExpect(modelEx().property("ph.hiddenNext", is(false)));
        }

        @Test
        @TestDataLoader("src/test/resources/ksbysample/webapp/email/web/mailsearch/testdata")
        public void nameのみで検索して1件ヒットする場合() throws Exception {
            mvc.nonauth.perform(TestHelper.postForm("/mailsearch", this.mailsearchFormName))
                    .andExpect(status().isOk())
                    .andExpect(content().contentType("text/html;charset=UTF-8"))
                    .andExpect(view().name("mailsearch/mailsearch"))
                    .andExpect(modelEx().property("page.number", is(0)))
                    .andExpect(modelEx().property("page.size", is(2)))
                    .andExpect(modelEx().property("page.totalPages", is(1)))
                    .andExpect(modelEx().property("ph.page1PageValue", is(0)))
                    .andExpect(modelEx().property("ph.hiddenPrev", is(true)))
                    .andExpect(modelEx().property("ph.hiddenNext", is(true)));
        }

        @Test
        @TestDataLoader("src/test/resources/ksbysample/webapp/email/web/mailsearch/testdata")
        public void typeのみで検索して2件ヒットする場合() throws Exception {
            mvc.nonauth.perform(TestHelper.postForm("/mailsearch", this.mailsearchFormType))
                    .andExpect(status().isOk())
                    .andExpect(content().contentType("text/html;charset=UTF-8"))
                    .andExpect(view().name("mailsearch/mailsearch"))
                    .andExpect(modelEx().property("page.number", is(0)))
                    .andExpect(modelEx().property("page.size", is(2)))
                    .andExpect(modelEx().property("page.totalPages", is(1)))
                    .andExpect(modelEx().property("ph.page1PageValue", is(0)))
                    .andExpect(modelEx().property("ph.hiddenPrev", is(true)))
                    .andExpect(modelEx().property("ph.hiddenNext", is(true)));
        }

    }

}

履歴

2015/06/17
初版発行。