広告

■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
                      2010年11月21日

    Java総合講座 - 初心者から達人へのパスポート
                  vol.208

                                セルゲイ・ランダウ
 バックナンバー: http://www.flsi.co.jp/Java_text/
■■■■■■■■■■■■■■■■■■■■■■■■■■■■■


-------------------------------------------------------
・現在、このメールマガジンは以下の2部構成になっています。
[1] 当初からのコース:vol.xxx(xxxは番号)が振られています。
   これは現在、中級レベルになっています。
[2] 2009年11月開講コース:xxx号(xxxは番号)が振られています。
   これは現在、初心者向けのレベルになっています。
・このメールマガジンは、画面を最大化して見てください。
小さな画面で見ていると、不適切な位置で行が切れてしまう
など、問題を起すことがあります。
・このメールマガジンに掲載されているソース・コード及び
文章は特に断らない限り、すべて筆者が著作権を所有してい
ます。また、これらのソース・コードは学習用のためだけに
提供しているものです。
-------------------------------------------------------


========================================================
◆ 01.Strutsのアプリケーション開発(プロジェクト:StrutsShop)
========================================================


さて、テストには正常な動きを確認するものだけでなく、何らかの
トラブル(問題)があったときにプログラムが適切に対処するかどうか
を確認するテスト(いわゆる異常系のテスト)というものもあります。

たとえば、RDBMS(StrutsShopのアプリケーションではMySQL)がダウン
していた、あるいは、起動されていなかった場合を考えてみましょう。
この場合は、アプリケーションがデータベースにアクセスすることが
できませんから正常な処理は行えません。
このような場合には普通、アプリケーションはユーザーに

--------------------------------------------------------
まことに申し訳ございません。
現在システムがダウンしています。
数時間程度で回復する予定ですので、数時間のちに再度
初めから操作をやり直してみて下さい。
あるいは、お急ぎの場合はTel:123-456-7890までお問合せ下さい。
--------------------------------------------------------

といった類のメッセージを返すとともに、ログに状況を記録して、オペ
レーターに異常を通知(たとえば警告音を鳴らし、オペレーター画面に
異常のメッセージを表示する等)します。

また、通知を受けたオペレーターは直ちにログを見て問題箇所を洗い出し
対処します。


では、この異常系のテスト・メソッドを書いてみましょう。

MySQLが起動していなかったためにエラーが発生したときには、
forwardの値が"systemerror"になり、systemerror.jspというWebページ
に進むことを確認します。
(実際にはここらへんの仕組みはまだ実装(コーディング)していませんが、
実装し忘れに気づかず、実装が終わっているつもりになっているという
想定で話を進めます。)


では異常系のテスト・メソッドをtestDbConnectionError()というメソッド名
で用意することにし、ItemListActionTest.javaを以下のように編集して
みましょう。

--------------------------------------------------------
package jp.co.flsi.lecture.struts;

import java.io.File;
import java.util.Vector;

import jp.co.flsi.lecture.struts.db.Item;
import jp.co.flsi.lecture.struts.db.ItemDbManager;
import jp.co.flsi.lecture.struts.db.StruShopDbException;

import servletunit.struts.MockStrutsTestCase;

public class ItemListActionTest extends MockStrutsTestCase {

   public void setUp() throws Exception {
         super.setUp();
         File contextDir = new File("D:\\JavaWorkSpace\\StrutsShop\\WebContent");
         setContextDirectory(contextDir);
   }

   public void tearDown() throws Exception {
         super.tearDown();
   }

/*
   public void testExecuteActionMappingActionFormHttpServletRequestHttpServletResponse() throws StruShopDbException {
      setRequestPathInfo("/itemlist");
      addRequestParameter("keyword", "製品001");
      addRequestParameter("categoryNum", "00001");
      actionPerform();
      verifyForward("success");
      verifyForwardPath("/itemList.jsp");
      ItemDbManager itemDbManager = new ItemDbManager();
      itemDbManager.connect();
      Vector<Item> itemList = itemDbManager.getDataByItemNameAndCatNum("製品001", "00001");
      Vector<Item> itemList2 = (Vector<Item>) getRequest().getAttribute("itemList");
      assertEquals(itemList.get(0).getNum(), itemList2.get(0).getNum());
      itemDbManager.disconnect();
      verifyNoActionErrors();
   }

//   前回(vol.207)最後の自由課題に対する回答例
   public void testExecuteActionMappingActionFormHttpServletRequestHttpServletResponse2() throws StruShopDbException {
      setRequestPathInfo("/itemlist");
      addRequestParameter("keyword", "製品100");
      addRequestParameter("categoryNum", "00001");
      actionPerform();
      verifyForward("success");
      verifyForwardPath("/itemList.jsp");
      ItemDbManager itemDbManager = new ItemDbManager();
      itemDbManager.connect();
      Vector<Item> itemList = itemDbManager.getDataByItemNameAndCatNum("製品100", "00001");
      Vector<Item> itemList2 = (Vector<Item>) getRequest().getAttribute("itemList");
      assertEquals(itemList.size(), 0);
      assertEquals(itemList2.size(), 0);
      itemDbManager.disconnect();
      verifyNoActionErrors();
   }
*/

   /**
    * RDBMS(MySQL)がダウンしている(起動されていない)場合の検証
    * (このテストの実行前にはRDBMS(MySQL)を停止しておくこと)
    */
   public void testDbConnectionError() throws StruShopDbException {
      setRequestPathInfo("/itemlist");
      addRequestParameter("keyword", "製品001");
      addRequestParameter("categoryNum", "00001");
      actionPerform();
      verifyForward("systemerror");
      verifyForwardPath("/systemerror.jsp");
      verifyNoActionErrors();
   }

}
--------------------------------------------------------



編集&保管が終わったら、MySQLを停止してからこのテスト・メソッドを実行
してみることにしましょう。

MySQLを停止するには

(1) (Windows XPの場合) 「スタート」ボタン→「コントロールパネル」を選択し、
「コントロールパネル」において、「パフォーマンスとメンテナンス」→「管理ツール」
を選択し、「サービス」をダブル・クリックして開きます。

(2) 「サービス」ウインドウの右側のリストの中で
MySQL
を探して選択(クリック)し、(現在「開始」の状態になっているはずなので)
左側の「サービスの停止」というところをクリックします。
(逆に停止後に再度起動する場合は「サービスの開始」をクリックすることになります。)

これでMySQLが停止しましたので、JUnitテストを実行してみましょう。

プロジェクト・エクスプローラーの中の「StrutsShop」(プロジェクト)配下の
「Javaリソース: src」配下のjp.co.flsi.lecture.struts(パッケージ)配下の
ItemListActionTest.javaを右クリックし、「実行」→「JUnitテスト」を
選択します。


すると、「JUnit」タブ配下のテスト結果を見てみると、テスト結果は
失敗だったことがわかりますね。

そこには、

junit.framework.AssertionFailedError: Cannot find forward 'systemerror'
 - it is possible that it is not mapped correctly.

というメッセージが表示されていますね。

つまり、forwardの値"systemerror"に対するマッピングの指定が正しくない
可能性があります。

これはテスト・メソッドの中の

verifyForward("systemerror");

という行を実行した結果、検証結果が失敗だったことを示すエラーです。


実は、RDBMSのダウンなどの例外発生に対する対応はまだstruts-config.xmlファイル
にも記述していないし、コーディングもしていなかったんですね。

という訳で、例外発生時にはforwardの値を"systemerror"にし、systemerror.jsp
のWebページに進むなどということは、struts-config.xmlファイルにも書いて
いないし、それに対応したコーディングもしていなかったんです。

(実際、単体テストの段階で、コーディングをし忘れていた箇所に気づくことは
時々あることで、Eclipseではコーディングし忘れを防ぐための機能も用意
されています。これを後ほどお話します。)


では、ログを見てみましょう。
「コンソール」タブ配下のログを見てみると、

java.net.ConnectException
MESSAGE: Connection refused: connect

というメッセージがあって、(RDBMSへの)接続(connect)に失敗していることが
わかります。
さらに、ずっと見ていくと

at jp.co.flsi.lecture.struts.db.DbManager.connect(DbManager.java:25)

という行があって、その中のDbManager.java:25のところをクリックすると、
DbManager.javaの25行目、すなわち

conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/STRUSHOP", "root", "rootpass");

という行に跳びますから、ここでDriverManagerのgetConnection()メソッドで
(JDBCドライバーを使って)データベースに接続しようとしたときにエラーになった
ことがわかります。
今回のテストはRDBMS(MySQL)を停止して行っているので、ここらへんのエラーの発生
は意図した通りです。

ところが、ログの一番最後のほうを見ると

jp.co.flsi.lecture.struts.ItemListAction.execute: Method return: <<<<< success

というメッセージがあることに気づきます。
これは、ItemListActionのexecute()メソッドの実行においてforwardの値を"success"
にしてメソッドを終了するときにログに書かせるようにコーディングしておいたもの
ですね。

つまり、RDBMSへの接続に失敗しているにもかかわらず、forwardの値を"success"に
しているわけで、forwardの値の設定に問題があることがわかります。

実際は、問題があるというよりも、例外発生時に対処するためのコーディングを
し忘れていたということなのですが、現実の開発現場ではコーディングする量が
とても多いために、ときどきコーディングのし忘れが発生することがあります。

多量の設計の仕様書を見ながら(オブジェクト指向方法論やシステムの分析・設計手法
については、ずっとのちに詳しくお話することにし、ここでは設計の話は省略します)
「これもコーディングしておかなくては。あれもコーディングしておかなくては。」
と忙しく考えているうちに、後回しにした部分を忘れてしまったりするのです。


そこで、後回しにした部分など、あとでコーディングしなければならない所には、
TODOというタグ(tag=名札,荷札)を付けたコメントを入れておくようにします。
たとえば、

// TODO Exceptionが投げられたときの処理をここに書く

というふうにメモ書き程度にコメントを入れておきます。
(TODOというのは「やるべきこと(to do)」という意味です。)

このTODOというコメントを入れておくと、Eclipseがその行の先頭にチェック・マーク
をつけて、目立たさせてくれるのです。また、右端のスクロール・バーの右側に
水色のマークを表示することによってTODOが存在する場所を示してくれます。
(これまでにEclipseが自動生成したソース・コードの中にTODOというコメントが
自動的に組み込まれていることで、すでに気づいている読者もいることと思います。)


そして、一通りコーディングが終わったと思ったときには、
メニュー・バーの「ウインドウ」→「ビューの表示」→「その他」を選択し、
表示された「ビューの表示」ウインドウの中で「一般」の配下の「タスク」を
選択し、「OK」ボタンをクリックします。
すると、画面の下のほうに「タスク」タブが表示されるので、それをクリック
することによって「タスク」ビューを開きます。

すると、そこにはTODOがリストされているはずです。
そのどれかの行をクリックすると該当するソース・コードの箇所が表示され、
TODOのコメント部分が反転するので直ぐに該当箇所がわかります。

こうして、TODOの残っている箇所を探しては未実装の部分をコーディング
していき、コーディングが終わった箇所はTODOのコメントを削除します。
最終的に「タスク」ビューの中にTODOが無くなったら、完璧に実装が終わった
ということになります。

┌補足─────────────────────────┐
TODO以外にもタグ(名札)を付けることができます。
メニュー・バーの「ウインドウ」→「設定」を選択し、
「Java」配下の「コンパイラー」配下の「タスク・タグ」
を選択してみて下さい。
すると、新規にタグを設定することもできることがわか
るでしょう。
また、すでにTODO以外にFIXMEとXXXというタグが用意されて
いることもわかりますね。
ちなみにFIXMEというのは優先度が高になっていますが、
このタグを使うと「タスク」ビューの中でどのように
表示されるか、実際に自分で試して確認してみて下さい。
また、(デフォルト)というのはソース・コードが自動生成
されるときに自動で組み込まれるタグを意味します。
└───────────────────────────┘


では、実際にこの例外に対する処理(handling)のコーディングを行って
いきましょう。


◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆


(次回に続く)



┏━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
★ホームページ:
      http://www.flsi.co.jp/Java_text/
★このメールマガジンは
     「まぐまぐ(http://www.mag2.com)」
 を利用して発行しています。
★バックナンバーは
      http://www.flsi.co.jp/Java_text/
 にあります。
★このメールマガジンの登録/解除は下記Webページでできます。
      http://www.mag2.com/m/0000193915.html
★このメールマガジンへの質問は下記Webページにて受け付けて
 います。わからない所がありましたら、どしどしと質問をお寄
 せください。
      http://www.flsi.co.jp/Java_text/
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━┛

Copyright (C) 2010 Future Lifestyle Inc. 不許無断複製