Spring Boot 1.4.x の Web アプリを 1.5.x へバージョンアップする ( その6 )( Thymeleaf を 2.1.5 → 3.0.6 へバージョンアップする2 )
概要
記事一覧はこちらです。
Spring Boot 1.4.x の Web アプリを 1.5.x へバージョンアップする ( その5 )( Thymeleaf を 2.1.5 → 3.0.6 へバージョンアップする ) の続きです。
- 今回の手順で確認できるのは以下の内容です。
- 前回に続き Thymeleaf 3 へのバージョンアップを行います。
参照したサイト・書籍
- Read Input hidden tag value from url of website using Jsoup in java
https://stackoverflow.com/questions/13071165/read-input-hidden-tag-value-from-url-of-website-using-jsoup-in-java
目次
- mainparts.html の head タグを th:fragment 化し、head-cssjs.html を削除する(その2)
- LibraryHelper#getSelectedLibrary を変更する
- 全ての各画面用 Thymeleaf テンプレートに
<!--/*@thymesVar id="..." type="..."*/-->
を記述する - WARN ログ
[THYMELEAF][main] Template Mode 'HTML5' is deprecated. Using Template Mode 'HTML' instead.
が出力されないようにする - clean タスク → Rebuild Project → build タスクを実行する
- メモ書き
手順
mainparts.html の head タグを th:fragment 化し、head-cssjs.html を削除する(その2)
残りの各画面用 Thymeleaf テンプレートを変更します。ファイル数が多いので、変更内容は書きません。また全て src/main/resources の下のファイルです。
- templates/booklist/booklist.html
- templates/booklist/complete.html
- templates/booklist/fileupload.html
- templates/confirmresult/confirmresult.html
- templates/lendingapp/lendingapp.html
- templates/lendingapproval/lendingapproval.html
- templates/sessionsample/confirm.html
- templates/sessionsample/first.html
- templates/sessionsample/next.html
- templates/springmvcmemo/beanValidationGroup.html
- templates/textareamemo/display.html
- templates/textareamemo/index.html
- templates/error.html
- templates/login.html
- templates/loginsuccess.html
Spring Boot 1.3.x の Web アプリを 1.4.x へバージョンアップする ( その26 )( jar ファイルを作成して動作確認する2 ) に記載した手順に従って画面を操作し、画面の表示がおかしなところがないことを確認します。
テストで
.andExpect(xpath(...))
を使用しているところを変更します。こちらも変更内容は書きません。全て src/test/java の下のファイルです。- ksbysample.webapp.lending.web.confirmresult.ConfirmresultControllerTest
- ksbysample.webapp.lending.web.lendingapp.LendingappControllerTest
- ksbysample.webapp.lending.web.lendingapproval.LendingapprovalControllerTest
clean タスク → Rebuild Project → build タスクを実行して “BUILD SUCCESSFUL” が出力されることを確認します。
LibraryHelper#getSelectedLibrary を変更する
src/main/java/ksbysample/webapp/lending/helper/library/LibraryHelper.java を リンク先の内容 に変更します。
src/test/java/ksbysample/webapp/lending/helper/library/LibraryHelperTest.java を リンク先の内容 に変更します。変更後、テストが成功することを確認します。
src/main/resources/templates/common/mainparts.html を リンク先の内容 に変更します。
Tomcat を起動して、図書館未選択時に “※図書館が選択されていません"、選択時に "選択中:…” が表示されることを確認します(画面キャプチャは省略します)。
全ての各画面用 Thymeleaf テンプレートに <!--/*@thymesVar id="..." type="..."*/-->
を記述する
以下の Thymeleaf テンプレートに
<!--/*@thymesVar id="..." type="..."*/-->
を追加します。変更内容は書きません。全て src/main/resources の下のファイルです。- templates/admin/library/library.html
- templates/booklist/booklist.html
- templates/booklist/fileupload.html
- templates/confirmresult/confirmresult.html
- templates/lendingapp/lendingapp.html
- templates/lendingapproval/lendingapproval.html
<!--/*@thymesVar id="..." type="..."*/-->
の記述は IntelliJ IDEA だと以下の操作で自動挿入できます。th:object
等で書いている変数の<!--/*@thymesVar id="..." type="..."*/-->
の記述がないと、以下の画像のように赤波線が表示されます。赤波線が表示されている変数にカーソルを移動して Alt+Enter を押すとコンテキストメニューが表示されますので、「Declare external variable in comment annotation」を選択します。
<!--/*@thymesVar id="..." type="..."*/-->
の記述が自動で挿入されますので、あとは type の部分を記入します。
WARN ログ [THYMELEAF][main] Template Mode 'HTML5' is deprecated. Using Template Mode 'HTML' instead.
が出力されないようにする
- src/main/resources/application.properties を リンク先の内容 に変更します。
clean タスク → Rebuild Project → build タスクを実行する
最後に clean タスク → Rebuild Project → build タスクを実行して、ここまでの変更で問題がないことを確認します。
メモ書き
th:block
は末尾に “/” が必ず必要
今回 src/main/resources/templates/common/mainparts.html を修正した時に、Thymeleaf 3 から末尾の “/” は不要になったと考えて、最初 <th:block th:replace="${links} ?: _">
と末尾に “/” を付けずに書いたのですが、これだときちんと動作しませんでした。
例えば「貸出希望書籍 CSV ファイルアップロード」画面(http://localhost:8080/booklist)を表示すると、<th:block th:replace="${links} ?: _"/>
の時は、HTML は以下のように出力されますが、
<head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>貸出希望書籍 CSV ファイルアップロード</title> <!-- Tell the browser to be responsive to screen width --> <meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport"> <!-- Bootstrap 3.3.4 --> <link href="/css/bootstrap.min.css" rel="stylesheet" type="text/css"> <!-- Font Awesome Icons --> <link href="/css/font-awesome.min.css" rel="stylesheet" type="text/css"> <!-- Ionicons --> <link href="/css/ionicons.min.css" rel="stylesheet" type="text/css"> <!-- Theme style --> <link href="/css/AdminLTE.min.css" rel="stylesheet" type="text/css"> <!-- AdminLTE Skins. Choose a skin from the css/skins folder instead of downloading all of them to reduce the load. --> <link href="/css/skins/_all-skins.min.css" rel="stylesheet" type="text/css"> <!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries --> <!-- WARNING: Respond.js doesn't work if you view the page via file:// --> <!--[if lt IE 9]> <script src="/js/html5shiv.min.js"></script> <script src="/js/respond.min.js"></script> <![endif]--> <!-- ここに各htmlで定義された link タグが追加される --> <link href="/css/fileinput.min.css" rel="stylesheet" type="text/css"> <style type="text/css"> <!-- .jp-gothic { font-family: Verdana, "游ゴシック", YuGothic, "Hiragino Kaku Gothic ProN", Meiryo, sans-serif; } .content-wrapper { background-color: #fffafa; } .noselected-library { color: #ff8679 !important; font-size: 100%; font-weight: 700; } .selected-library { color: #ffffff !important; font-size: 100%; font-weight: 700; } .box-body.no-padding { padding-bottom: 10px !important; } .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; font-size: 90%; } .has-error { background-color: #ffcccc; } --> </style> <!-- ここに各htmlで定義された style タグが追加される --> <style type="text/css"> <!-- .callout ul li { margin-left: -30px; } --> </style> </head>
<th:block th:replace="${links} ?: _">
のように末尾の “/” を付け忘れると、<style type="text/css">
から下が表示されなくなります。しかも何のエラーも出ません(画面の背景色が青くなったのでおかしいことに気づきました)。
<head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>貸出希望書籍 CSV ファイルアップロード</title> <!-- Tell the browser to be responsive to screen width --> <meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport"> <!-- Bootstrap 3.3.4 --> <link href="/css/bootstrap.min.css" rel="stylesheet" type="text/css"> <!-- Font Awesome Icons --> <link href="/css/font-awesome.min.css" rel="stylesheet" type="text/css"> <!-- Ionicons --> <link href="/css/ionicons.min.css" rel="stylesheet" type="text/css"> <!-- Theme style --> <link href="/css/AdminLTE.min.css" rel="stylesheet" type="text/css"> <!-- AdminLTE Skins. Choose a skin from the css/skins folder instead of downloading all of them to reduce the load. --> <link href="/css/skins/_all-skins.min.css" rel="stylesheet" type="text/css"> <!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries --> <!-- WARNING: Respond.js doesn't work if you view the page via file:// --> <!--[if lt IE 9]> <script src="/js/html5shiv.min.js"></script> <script src="/js/respond.min.js"></script> <![endif]--> <!-- ここに各htmlで定義された link タグが追加される --> <link href="/css/fileinput.min.css" rel="stylesheet" type="text/css"></head>
HTML の meta タグ等は末尾を />
にする必要はなくなりましたが、th:block
は <th:block ... />
のように必ず末尾に “/” を付けましょう。
.andExpect(...)
は lambda 式で .andExpect(mvcResult -> {...})
と書けば AssertJ が利用できる
今回久しぶりに ResultMatcher のクラスを作成して気づきましたが、.andExpect(...)
内に指定する式は、
- MvcResult 型の引数を1つ渡して、戻り値が void 型の lambda 式でよい。
- lambda 式の中で AssertionError の例外が throw されればテストが失敗する。
ということに気づきました(org.springframework.test.web.servlet.ResultMatcher インターフェース参照)。
ということは、.andExpect(mvcResult -> {...})
の形式で書けば、AssertJ を利用することができます。例えば、src/test/java/ksbysample/webapp/lending/web/lendingapproval/LendingapprovalControllerTest.java の lendingAppIdパラメータで指定されたデータが登録されており承認権限を持つユーザならば貸出承認画面が表示される()
メソッドを以下のように変更して、
@Test @TestData("web/lendingapproval/testdata/001") public void lendingAppIdパラメータで指定されたデータが登録されており承認権限を持つユーザならば貸出承認画面が表示される() throws Exception { mvc.authSuzukiHanako.perform(get("/lendingapproval?lendingAppId=105")) .andExpect(status().isOk()) .andExpect(content().contentType("text/html;charset=UTF-8")) .andExpect(view().name("lendingapproval/lendingapproval")) .andExpect(model().hasNoErrors()) .andExpect(html("#lendingapprovalForm > div > div > table > tbody > tr").count(3)) .andExpect(mvcResult -> { assertThat(Jsoup.parse(mvcResult.getResponse().getContentAsString()) .select("#lendingapprovalForm > div > div > table > tbody > tr > td:eq(1)")) .extracting(Element::text) .containsOnly("978-4-7741-5377-3", "978-4-7973-4778-4", "978-4-87311-704-1"); }); }
テストを実行すると、問題なく成功します。
.containsOnly(...)
に記述する要素を1つ削除してテストが失敗するように変更すると、
@Test @TestData("web/lendingapproval/testdata/001") public void lendingAppIdパラメータで指定されたデータが登録されており承認権限を持つユーザならば貸出承認画面が表示される() throws Exception { mvc.authSuzukiHanako.perform(get("/lendingapproval?lendingAppId=105")) .andExpect(status().isOk()) .andExpect(content().contentType("text/html;charset=UTF-8")) .andExpect(view().name("lendingapproval/lendingapproval")) .andExpect(model().hasNoErrors()) .andExpect(html("#lendingapprovalForm > div > div > table > tbody > tr").count(3)) .andExpect(mvcResult -> { assertThat(Jsoup.parse(mvcResult.getResponse().getContentAsString()) .select("#lendingapprovalForm > div > div > table > tbody > tr > td:eq(1)")) .extracting(Element::text) .containsOnly("978-4-7741-5377-3", "978-4-7973-4778-4"); }); }
テストが失敗して、かつ失敗の原因が表示されます。
.andExpect(...)
の中で AssertJ を使うことは可能です、という話でした。
ソースコード
LibraryHelper.java
public String getSelectedLibrary() { String result = null; LibraryForsearch libraryForsearch = libraryForsearchDao.selectSelectedLibrary(); if (libraryForsearch != null) { result = "選択中:" + libraryForsearch.getFormal(); } return result; }
- getSelectedLibrary メソッドで図書館が選択されていない場合には “※図書館が選択されていません” ではなく null を返すようにします。
LibraryHelperTest.java
@Test public void testGetSelectedLibrary_図書館が選択されていない場合() throws Exception { given(libraryForsearchDao.selectSelectedLibrary()).willReturn(null); String result = libraryHelper.getSelectedLibrary(); assertThat(result).isNull(); }
testGetSelectedLibrary_図書館が選択されていない場合()
テストメソッド内のassertThat(result).isEqualTo("※図書館が選択されていません");
→assertThat(result).isNull();
ヘ変更します。
mainparts.html
<!-- Navbar Right Menu --> <div class="navbar-custom-menu"> <p class="navbar-text" th:with="selectedLibrary=${@libraryHelper.getSelectedLibrary()}" th:classappend="${selectedLibrary} ? 'selected-library' : 'noselected-library'" th:text="${selectedLibrary} ?: _">※図書館が選択されていません</p> <ul class="nav navbar-nav"> <li><a href="/logout">ログアウト</a></li> </ul> </div>
th:with="selectedLibrary=${@libraryHelper.getSelectedLibrary()}"
を追加します。th:classappend
の中の${#strings.startsWith(@libraryHelper.getSelectedLibrary(), '※')}
→${selectedLibrary}
に変更します。また'noselected-library' : 'selected-library'
→'selected-library' : 'noselected-library'
に変更します。th:text
の中の${@libraryHelper.getSelectedLibrary()}
→${selectedLibrary} ?: _
に変更します。
application.properties
spring.freemarker.cache=true spring.freemarker.settings.number_format=computer spring.freemarker.charset=UTF-8 spring.freemarker.enabled=false spring.freemarker.prefer-file-system-access=false spring.thymeleaf.mode=HTML valueshelper.classpath.prefix=
spring.thymeleaf.mode=HTML
を追加します。
履歴
2017/05/25
初版発行。