05.設計7: テスト
概要
ここでは単体テストと、総合テストなどのテストについてみていきます。
設計というより、どんなツールを使っていくかということを見る感じになると思います。
ところで、
単体テストに抵抗を持っている方はいらっしゃいますか?
よくある意見に、「単体テストは稼働が掛かる」、「意味がない」という意見を持つ方がいらっしゃいます。
しかし、実際のところ、単体テストをしない方が全体の稼働がかかります。
それは運用フェーズに入った後に発生した不具合の対処時間も含めての話です。
たしかに、単体テストフェーズがない分、結合や総合テストのフェーズに入るのは早くなります。
しかし、よくわからない不具合が連発し、結局、その調査や修正に時間がかかってしまいます。
もう一つ、「修正が入ったときに、単体テストも修正しないといけないので稼働がかかる」という方もいらっしゃいます。
この意見も全体をとらえているとは言い難いと思います。
むしろ、たくさんの単体テストを再度実行するので、予想できなかった個所のバグも発見できます。
また、単体テストも修正しないといけないのは確かですが、直しているうちに修正の設計の間違いに気づくことも
あります。
単体テストは必須だと思っていただいてよいと思います!
ちなみに、WEBのControllerのテストも単体テストで記述した方が早くデバッグできます。
JUnit
Javaで単体テストを行うときはJUnitをたいてい使います。
他にもTestNGなど、有名なテストツールが存在しますが、本サンプルではJUnitを使います。
【JUnit 使用時のログの設定について】
ログはlogbackを使用していますが、本サンプルではmavenを使用しなかったため、設定ファイルを分割したり、
条件分岐でログ設定ファイルのパスを変えたりしています。
logbackでは、デフォルトでは「logback.xml」が設定ファイルとなりますが、「logback-test.xml」というファイルがあると
そのファイルが使われます。そして、mavenでJUnit やeclipseのサーバを実行すると、
logback-test.xmlをクラスパスに置いて実行するようで、テスト用のログ設定になります。
さらにmavenは、WARを作成するときには「logback-test.xml」ファイルはWARに含めないため、「logback.xml」が使用されます。
設定をうまく書き分けることで、WAR実行時はログをファイルに出力し、
eclipseで起動するときはログをコンソールに出力する、ということが可能ということです。
かなり便利そうですので、mavenなどのツールは使った方がよさそうです。
Spring MVC Test Framework
Springのテストツールです。
JUnitなどと組み合わせて使えます。
また、eclipseでtomcatを起動したときのようにControllerを実行できます。
そのため、Controllerのデバッグを簡単に行えます。
JUnitだけですと、フィルタやSpringMVCのサーブレットを起動できないだけでなく、SpringSecurityも起動できません。
ですがSpringのテストツールを使うと、すべてWEBで起動したときのようにできます。
少し工夫は必要ですので、ソースの「TestMemberController」クラスを見てみてください。
使い方がイメージできるように、使用例を書いてみようと思います。
【Spring MVC 単体テストの使用例 (一部抜粋)】
@Before
public void setup() throws Exception {
initDb();
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac)
//SpringSecurityのフィルタを追加します。
.addFilters(this.springSecurityFilterChain)
.build();
}
/**<pre>
* 正常な会員情報編集画面へのアクセス。
* </pre>
* @throws Exception
*/
@Test
public void testInputNormal() throws Exception{
//ログインします。自作メソッドです。
login("taro", "taro");
//画面(Controller)を呼び出します
MvcResult result = this.mockMvc.perform(get("/cust/member/edit/input.html?member.id=1")
.session(this.mockSess)
)
.andDo(print())
.andExpect(status().isOk())
.andReturn();
//結果の取得
ModelAndView mv = result.getModelAndView();
Map<String, Object> model = mv.getModel();
Member m = (Member) PropertyUtils.getProperty(model, "form.member");
//結果の確認
assertEquals("cust/member-Edit-Input", mv.getViewName());
assertEquals(1, m.getId());
}
初めて見ても、メソッド名からなんとなく何をやっているか分かりますよね?
しかも、この単体テストをデバッグモードで起動すれば、Controllerをデバッグできます。
eclipseの「サーバーでデバッグ」でデバッグしようとすると、いちいちログインID/PWを入力し、
目的の画面まで遷移しないといけません。
また、tomcatの起動まで何十秒もかかります。
これは効率が相当悪いです。
単体テストを少し書いてControllerのデバッグを行った方が効率が良いことが分かると思います。
DbUnit
DbUnit は、DBを使った単体テストをするためのツールです。
具体的には、テーブルにデータを登録する機能と、更新後にテーブルの値が正しいことを確認する機能などが
あります。
使い方等は、ソースの「TestMemberDao」クラスを見てみてください。
本サンプルでは、投入するテーブルのデータは、Excelで記述することにします。
【いくつかのTips】
・Excel更新後は、eclipseのパッケージ名を選択してF5キーを押してください。(もしくは右クリック⇒リフレッシュ)
そうしないと、実行フォルダ内のExcelファイルが上書きされず、古いファイルがDBにインポートされる可能性があります。
・全体の書式を文字列にしておいてください。
・日付の記述は「YYYY-MM-DD HH:mm:ss」にする必要があります。
・new DatabaseConnection(this.dataSource.getConnection(), null, false);について
今回はPostgresですが、Oracleなどを使用する場合、第二引数のスキーマを指定しないとうまく動かない場合があります。
djUnit
カバレッジを計測するためのツールです。
カバレッジとは、全ソースのステップ数のうち、JUnit 単体テストで通過したステップ数の割合(%)です。
djUnitは、JUnitと連携して自動で割合を計測してくれます。
視覚的に結果も分かるので便利です。
実際のソースに、JUnitでどこを通らなかったのかが表示されます。
目標としては100%を目指した方が良いですが、すべてのクラスで100%は難しいので、最低でも60~80%を
目指すのが良いかもしれません。
もちろん、詳細設計の仕様を満たしているかもテストしていく必要があります。
カバレッジの%を上げることが目的になってはいけません。
【設定方法】
次に、プロジェクトを右クリック⇒プロパティ で開いて、設定します。
【実行方法】
単体テストのソースフォルダを選択して、実行の構成を開くと、すべての単体テストが実行されます。
新規作成して、「-noverify」を指定して起動するようにします。(初回だけです)
【実行結果のサンプル】
カバレッジの表示
単体テストで通らなかったコード部分に線が付く
総合テストと運用テスト
単体・結合テストの後のテストのことも軽く触れます。
本サンプルでは行いませんが、通常は、総合テストと運用テストを行うと思います。
言い方や定義はベンダさんによっても違うと思いますが、主に以下のことをすると思います。
・総合テスト ・・・要件定義などから実際の運用に近いフローを抽出し、テストする。
例えば、会員の登録 ⇒ サービスの購入 ⇒ 会員の退会 のような
運用に近いライフサイクルを通るようなテストを行い、全体的に問題ないかを見ます。
既に単体・結合が終わっているので、ある関数の動作が問題ないか?、関数間の連携は取れているか?
のような細かなテストではなく、あくまで運用したときに問題が発生しないかを見ていきます。
例えば、会員登録とサービス購入は問題なく動作するが、サービス解約するとゴミデータが残って
会員の退会ができない、といった運用目線の不具合はこのテストで見つかることになります。
・運用テスト・・・負荷テスト、連続運転テストなどと呼ぶこともあるかと思います。
実際の運用者に実務と並行して1か月間くらい使ってもらったり、JMeterなどで連続的に負荷をかけて
問題なく動作するか?、などをテストしていきます。
「問題なく」をどう判定するかというと、サーバがダウンしないこと、CPU負荷100%が続かないこと、
メモリ使用率100%が続かないこと、などを見ていきます。
1か月くらい負荷を掛けて、HDDの容量などがいっぱいにならないことを見ることもあるかもしれません。
本サンプルでの方針
【テストの目標値(どれだけやったらテスト終了とするか?)】
本サンプルでは、単体テスト、クラス間の結合テストをJUnitで行うことを目標とします。
カバレッジの%の目標は人やベンダさんによって意見が分かれると思います。
できれば100%、最低でも60%以上を目指したいと思ったのですが、サンプルでは目標値を満たしていません。
【テスト対象】
Dao、ビジネスロジック、Controller、共通関数について、それぞれ単体テストを作る方針にしました。
Controllerのテストでは、最初はDao、ビジネスロジックができていないので、ビジネスロジックのスタブでテストし、
完成したら後から完成品と入れ替える方針です。
完成品と入れ替えた後は、そのままJUnitを実行するだけですので簡単です。
サンプルでは入れ替え後の状態にしているので、途中のスタブの状態が見えませんが。
Created Date: 2015/03/28