ハンズオンセクション 2
概要
さて、ハンズオンの続きです。
"データ登録をしてConditionBeanで基本の検索" をしていきましょう。
事前準備
org.docksidestage.handson.exercise.HandsOn02Test クラスを src/test/java (Testクラス置き場) に作成してください。 このクラスは UnitContainerTestCase を継承します。また、ERDを開いておくと良いでしょう。
マスタデータの登録
ここでいうマスタデータとは、会員ステータスなどの固定的なテーブルを指します。(開発環境でも、本番でも変化しないようなテーブル、例えば区分値など)
マスタデータがないことを確認
localdb/login-mysql-root.bat|sh で MySQL にログインして、会員ステータスが一件も存在しないことを確認してください。
e.g. 会員ステータスの件数を取得 @SQL
-- MaihamaDBに接続
use maihamadb;
-- 会員ステータスのカウント検索
select count(*) from MEMBER_STATUS;
マスタデータを配置して再びReplaceSchema
tutorial/material/dbflute/playsql-loaddata-common.zip (commonの方です) を解凍して、10-master.xls を、以下のように配置して、ReplaceSchemaを実行してください。
e.g. マスタデータ用のエクセルファイルの配置場所 @Directory
PROJECT_ROOT
|-...
|-dbflute_maihamadb
| |-dfprop
| |-...
| |-playsql
| | |-data
| | | |-common
| | | |-xls
| | | |-10-master.xls
| | |-replace-schema-00-system.sql
| | |-replace-schema-10-basic.sql
...
マスタデータが入ったことを確認
先ほどの会員ステータスのカウント検索の結果が 0 でないことを確認してください。
テストメソッドの作成
こんどは、テストデータの登録ですが、先にテストメソッドを書いて、テストデータが入っていることをアサートしてみましょう。 まだデータを登録する前ですから、テストは落ちるはずです。
テストデータがあることをアサートするテスト
HandsOn02Test に、会員(MEMBER)のデータをアサートするテストを書いて "落ちること" を確認してください。テストメソッド名は test_existsTestData() とします。
このプロジェクトでは、メソッド名が test で始まることで、それがテストメソッドであると認識されます。 今後、テストを作成する際は、必ず "test何々" という形式でメソッドを作成してください。
テストメソッドの雛形
セクション1で設定した、補完テンプレートが入っていることを前提とします。 ここでは、テストメソッドの雛形を作る補完を使います。IDEによって若干補完文字が違うのでご注意を。
- Eclipse
- _test ※ドキュメント上の説明は、こちらをベースに書いています
- IntelliJ
- _utest
_test と打って ctrl + space して enter するとテストメソッドの雛形が作成されます。
e.g. テストメソッドのコード補完 @Java
public class HandsOn02Test extends UnitContainerTestCase {
_test // _test と入力して ctrl + space して enter
}
--
public class HandsOn02Test extends UnitContainerTestCase {
public void testname() throws Exception { // name の部分が入力モードに
// ## Arrange ##
// ## Act ##
// ## Assert ##
}
}
テストコードのお約束コメントの Arrange や Act のコメントが付与されているかと思います。 実はこれは、セクション1で DBFlute のエディターテンプレートを設定したからです。
メソッド名とBehaviorの宣言
テストメソッド名は、"test_existsTestData()" とし、MemberBhv を private のインスタンス変数として宣言してください。 Lasta Di では、@Resource というアノテーションを付けるとDI対象フィールドとなります。(_ladany という補完を使うと少し楽になるかも)
e.g. test_existsTestData() の実装準備 @Java
public class HandsOn02Test extends UnitContainerTestCase {
@Resource
private MemberBhv memberBhv;
public void test_existsTestData() throws Exception {
// ## Arrange ##
// ## Act ##
// ## Assert ##
}
}
テストコードの実装
そして、絞込み条件なしのシンプルな検索処理を実装してみましょう。(いわゆる全件検索です)
Behavior の selectCount() を使って select count(*) を実行し、assertTrue()メソッドを使ってアサートしてみましょう。 (assertTrue() は、"assT" とまで打って ctrl + space すれば補完できます)
テストコードの実行
その UnitTest (test_existsTestData()) を実行して、落ちることを確認してください。 テストコードが正しく実装されていれば、テストデータはまだ入っていないので例外が発生し、JUnitのバー表示が赤くなる (redになる) はずです。
UnitTest は red から
ちなみに UnitTest は、最初 red になる状態で作っておいて、正しく修正した後に green になるようにしておくと良いです。 事前に何も実行せずに正しく修正してから green になっても、その UnitTest のプログラム自体がちゃんと正しいチェックをしているのかわからないからです。 (もしかしたら、別に正しい修正をする前から green になっちゃってたのかも...)
もし、Eclipse なら
テストを実行する際は、そのテストメソッドの中にカーソルがある状態で ctrl + 0 を押すと、そのテストが実行されます。これは Quick JUnit のショートカットです。
テストデータの登録
テストデータを配置して再びReplaceSchema
tutorial/material/dbflute/playsql-loaddata-ut.zip (さっきと違ってこんどは ut の方です) を解凍して、20-member.xls と 30-product.xls を、以下のように配置してください。
e.g. テストデータ用のエクセルファイルの配置場所 @Directory
PROJECT_ROOT
|-...
|-dbflute_maihamadb
| |-dfprop
| |-...
| |-playsql
| | |-data
| | | |-common
| | | | |-xls
| | | | |-10-master.xls
| | | |-ut
| | | |-xls
| | | |-20-member.xls
| | | |-30-product.xls
| | |-replace-schema-00-system.sql
| | |-replace-schema-10-basic.sql
...
再び ReplaceSchema を実行
さあ、実行してみてください。
ログを読んでみましょう
落ちるはずです。ログを追って、何が起きたのか? を把握してみましょう。 コンソールが見づらければ、DBFluteクライアント(dbflute_maihamadb)の log/dbflute.log を見るとよいでしょう。
"例外の翻訳" を意識してみましょう。
e.g. exception translation model @Model
catch catch catch
<---- 例外 / 例外 / 例外 / 例外
\ / \ / \ / |
o | | | \
/|\ -> A -> B -> C -> D ----> D'
/ \
(DBFlute) (ReplaceSchema) (MySQL)
ログを読むものハンズオンです。DBFluteタスクが必ず成功するとは限りません。 設定にミスがあれば遠慮なく落ちますので、落ちたときの分析方法をしっかりと知っておきましょう。
ちなみに、問題分析と問題解決をごちゃまぜにしないようにしましょう。 まずは分析が大切です。分析の前に解決方法のことばかりを考えてると、解決のヒントを見逃してしまいがちで本末転倒です。
defaultValueMap.dataprop
原因わかりましたでしょうか?...どういう風に解決していきましょうか。
20-member.xls を開いてみてみてください。NotNull制約のついている共通カラムのデータがありません。 ここでは、(DB上ではなく)ReplaceSchemaにデフォルト値を設定して解決しましょう。 同じディレクトリに以下の内容で defaultValueMap.dataprop というファイルを作成して、ReplaceSchemaを再実行してみましょう。
e.g. 共通カラムのデフォルトテストデータの設定 @defaultValueMap.dataprop
map:{
; REgISTER_DATETIME = sysdate
; REgISTER_USER = foo
; UPDATE_DATETIME = sysdate
; UPDATE_USER = foo
; VERSION_NO = 0
}
テストが通ることを確認
再度テストを実行し、テストが通ってJUnitのバー表示が green になることを確認してください。
テストデータの閲覧
さて、ここでじっくり ConditionBean について学んでみましょう。
はじめてのConditionBean
せっかくなので、どのようなデータが入っているかさらに見てみましょう。 以下のテストを作成してください。(メソッド名の先頭にtestを付けましょう。メソッド名はいい感じに...)
- 会員名称がSで始まる会員を検索 (これはタイトル、この中にも要件が含まれている)
-
- 会員名称の昇順で並べる (これは実装要件、Arrange or Act でこの通りに実装すること)
- (検索結果の)会員名称がSで始まっていることをアサート (これはアサート要件、Assert でこの通りに実装すること)
- "該当テストデータなし" や "条件間違い" 素通りgreenにならないように素通り防止を (今後ずっと同じ)
- 会員IDが1の会員を検索
-
- 一件検索として検索すること
- 会員IDが 1 であることをアサート
- ※修行++: 試しに一時的に会員IDを99999で検索して、発生する例外のメッセージを読んでみましょう。ぜひ、DBFluteの例外メッセージの特徴を知りましょう。
- 生年月日がない会員を検索
-
- 更新日時の降順で並べる
- 生年月日がないことをアサート
## Act ## がメインコードのつもりで
本来、UnitTestというのは、mainコード (src/main/java) を、testコード (src/test/java) で呼び出して、検証をするというものですが、セクション5までは、シンプルな形で DBFlute を体験してもらうために、Act が main コード と想定して、Assert をしていきます。
e.g. セクション5までは Arrange, Act, Assert の Act がメインコード @Java
## Arrange ##
## Act ##
// ここが本番サーバーで動くコードのつもり (メインコード)
## Assert ##
...
log()でテスト自体の挙動の確認を
アサートだけでなく、テストコード自体が想定通りか?を確認する意味でのログの出力も実装すると良いでしょう。 テストコード内では、log()メソッド が利用できます。 ログに何を出力するかは自由です。ただ、ログも綺麗に 出しましょう。 Slf4jライクなプレースホルダー "{}" も使えるので積極的に使っていきましょう。
e.g. log()メソッドでSlf4jライクなプレースホルダー "{}" を @Java
log("memberName: {}, birthdate: {}", memberName, birthdate);
todoコメントを使っていきましょう
もし、わからなくて保留して先に進むときや質問があるときは、後で忘れずに戻って来れるようにするために、todoコメントを活用しましょう。 セクション1で入れた補完テンプレートの _todo を使えば自分の名前や日付などが自動で入りますのでぜひ使ってみましょう。 (todoコメントは、誰の?いつ頃の?というのが後でわからなくなりがちなので)
e.g. 保留するときはコード上にtodoコメントを! @Java
// TODO jflute できたっぽいけど、ここなぜ動くのかわからないので後で調べる (2019/04/07)
cb.query().setMemberName_LikeSearch...
cb.query().addOrderBy_...
...
コード補完を駆使して実装
DBFluteは、Java (コンパイル言語) の良さを最大限活かしていくことに重きを置いています。
ぜひ、ショートカットとコード補完を駆使して、気持ちよく実装していきましょう。 最初は全然ゆっくりでOKです。ハンズオンでじっくり手に馴染ませて、本番(業務の実装)でスピーディーに実装できればいいわけなのです。
ConditionBeanの機能を探したいときはどうする?
ぜひ、"ディベロッパーのFAQ" をじっくりお読みください。
まずは、DBFluteに関係なく やりたいことを明確にすること これを重要視しています。 それさえハッキリすれば、その一つ一つの "目的" から、おのずと機能が探しやすくなっているはずです。
(ConditionBeanを覚えるためにやっているというのに...) いきなりConditionBeanのことを考えるのではなく、要件をよく読み、ERDのテーブル構造と照らし合わせて検索イメージを湧かせることが大切です。
すでに何十人の人にハンズオンを行っていますが、(もちろん経験の差もありますが) 自力で機能を見つけられる方と苦戦される方の大きな違いは、そこにあると感じています。 見つけ方、それがわかってしまえば、機能を丸暗記する必要はないのです。
これは DBFlute に限らない話で、覚えることのあまりに多過ぎるこの世界でやっていくための、とても重要なスキルと言えると考えていますので、ぜひここで意識してみてください
模範解答的な
模範解答的な実装を紹介しています。(参考までに)
次のセクション
さて、次のセクションへ