広告

■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
                      2010年10月25日

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

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


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


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

StrutsTestCaseにおけるテストの実行方法は、やはりJUnitの場合と同じで
(JUnitにおけるテストの実行方法を忘れた人はvol.161を復習のこと)、例えば
前回のItemListActionTestを実行するには、プロジェクト・エクスプローラーの
中でItemListActionTestを右クリックし、「実行」→「JUnitテスト」を選択する
ことになります。
つまり、StrutsTestCaseにおけるテストはTomcatを起動せずに(Tomcatから独立
した状態で)行うことになります。

したがって、アプリケーションにTomcatの起動が前提になっている部分があると
うまく動作しないことになります。

という訳で、Tomcatの起動が前提になっている部分を事前に少し手直ししておき
ましょう。


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

まず最初に、Tomcatのクラスパス
C:\pleiades\tomcat5.5\common\classes
はTomcatを起動していなければ有効にならないことに注意しましょう。
これはStrutsのアプリケーションがTomcatから呼び出されている場合には
参照されますが、そうでない場合には有効ではありません。

このフォルダーの中にはLog4jのプロパティー・ファイル(log4j.properties)
を入れてありますが、ItemListActionTestの実行時にはこのファイルにアクセス
できないことになり、Log4jのログ出力に支障が生じてしまいます。

その対処法としては、ItemListActionTestを実行するときに参照される
クラスパスにこのフォルダーを追加する
(プロジェクト・エクスプローラー内のStrutsShop(プロジェクト)を
右クリック→「ビルド・パス」→「ビルド・パスの構成」を選択し、
「ライブラリー」タブをクリックして「外部クラス・フォルダーの追加」ボタン
をクリックし、該当するフォルダーを選択)という方法もありますが、今回は
もっと簡単に
C:\pleiades\tomcat5.5\common\classes
のフォルダー内にあるlog4j.propertiesを
C:\JavaWorkspace\StrutsShop\build\classes
のフォルダー内にコピーしておきましょう。
こちらのフォルダーはItemListActionTestなど
Tomcatから独立して実行するクラスに対してもクラスパスとして有効です。


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

もう一つ注意しなければならないことは、Tomcatは実行しないので、データベース
の接続プールが使用できないことです。

現在のところ、jp.co.flsi.lecture.struts.dbパッケージのDbManagerは
この接続プールを使用することを前提に作られているので修正が必要です。
接続プールの代わりにDriverManagerで直接データベース接続を取得する
ようにソースを修正しておきましょう。

というわけで、jp.co.flsi.lecture.struts.dbパッケージのDbManagerを
下記のように修正して下さい。

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

import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Connection;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;

import org.apache.log4j.Logger;

public class DbManager {
   protected Connection conn = null;
   private static Logger logger = Logger.getLogger(DbManager.class);

   public void connect() throws StruShopDbException {
      logger.info("Start ...............");
      try{
         if (conn == null || conn.isClosed()) {

            if ("junit".equals(System.getProperty("ut"))) {
                  Class.forName("com.mysql.jdbc.Driver");
                  conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/STRUSHOP", "root", "rootpass");
                  conn.setAutoCommit(false);
               return;
            }

            Context initCtx = new InitialContext();
            if(initCtx == null) throw new StruShopDbException("Error: InitialContext could not be generated!");
            DataSource ds = (DataSource) initCtx.lookup("java:comp/env/jdbc/STRUSHOP");
            if (ds != null) {
               conn = ds.getConnection();
               if(conn == null) throw new StruShopDbException("Error: Connection does not exist!");
               else logger.info("Connection has been gotten.");
            }
            else {
               throw new StruShopDbException("Error: DataSource does not exist!");
            }
         }
      } catch (NamingException e) {
         logger.error(e, e);
         throw new StruShopDbException("Error: Connect() failed!", e);
      } catch (SQLException e) {
         logger.error(e, e);
         throw new StruShopDbException("Error: Connect() failed!", e);
      } catch (StruShopDbException e) {
         logger.error(e, e);
         throw new StruShopDbException("Error: Connect() failed!", e);
      }
      catch (Throwable e) {
         logger.error(e, e);
      }
      finally {
         logger.info("End ...............");
      }
   }

   public void disconnect() {
      logger.info("Start ...............");
      try {
         if (conn != null && !conn.isClosed()) conn.close();
      } catch(SQLException e) {
         logger.error(e, e);
      }
      catch (Throwable e) {
         logger.error(e, e);
      }
      finally {
         logger.info("End ...............");
      }
   }

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


変更したところは、

            if ("junit".equals(System.getProperty("ut"))) {
                  Class.forName("com.mysql.jdbc.Driver");
                  conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/STRUSHOP", "root", "rootpass");
                  conn.setAutoCommit(false);
               return;
            }

という6行だけですね。

ここで、SystemクラスのgetProperty()というメソッドは、Javaのシステム・プロパティー
の値を取得するためのものです。
このシステム・プロパティーというのはOSの環境変数に似たもので、これまでに
使ったプロパティー・ファイルと同様に複数のプロパティー(キーと値の組み合わせ)
から構成されます。
システム・プロパティーには実際に環境変数も取り込まれますが、それ以外にJava独自
のプロパティーも持っていますし、アプリケーションごとに独自のプロパティーを
追加することもできます。

そして、上のソース・コードのように
System.getProperty("ut")
というメソッド呼び出しを行うと、システム・プロパティーから"ut"というキーの
値が取り出されることになります。

┌補足─────────────────────────┐
システム・プロパティーのうちJava独自のプロパティーとして
よく使われるものには例えば、

"file.separator"
(パスの指定のときなどに使うディレクトリーの区切り文字。
例えばWindows環境では
\pleiades\tomcat5.5\common\classes
の中の"\"の文字がこの区切り文字であり、UNIXやLinuxでは
"\"ではなく"/"が使われる。)



"line.separator"
(ファイルの中で改行のマークとして使われる区切り文字。
(区切り文字といっても文字として画面上に表示されるわけ
ではない)これもやはりOSによって異なる。例えばWindows環境
ではCR + LFであり、UNIXやLinuxではLFである。
CRやLFについてはvol.016(または2009年11月開講コース016号)を
参照のこと。)

があります。
これらの区切り文字はアプリケーションの中で一度、

String FILE_SEPRATOR = System.getProperty("file.separator");
String LINE_SEPRATOR = System.getProperty("line.separator");

のように変数(通常はfinal指定して定数扱いする)に代入して
おいて、以後これらの変数を使うようにすれば、実行環境のOS
に依存せずに済みます。
└───────────────────────────┘

そこで、このJUnitを使った単体テストをするときは、予めシステム・プロパティーに
"ut"というキーのプロパティーを追加して、そのキーに"junit"という値を設定して
おけば、上記のif文が有効になり、DriverManagerを使ったデータベース接続の取得
が行われることになる訳です。
(ちなみにシステム・プロパティーに"ut"というキーが存在しないときは、
System.getProperty("ut")のメソッド呼び出しの戻り値はnullになります。
上のif文でSystem.getProperty("ut").equals("junit")とせずに
"junit".equals(System.getProperty("ut"))としているのは、
こうしておけばSystem.getProperty("ut")がnullを返しても
NullPointerExeptionが発生せずに済むからです。)

この"ut"というキーを追加するためには環境変数を使って設定してもいいのですが、
もっと簡単にはJVMのオプションで設定することができます。

具体的には、JVM(javaコマンド)のオプションの一つとして

-Dproperty=value

というのがあり、このうちpropertyの部分にプロパティーのキーを指定し、
valueの部分にその値を指定します。
(JVM(javaコマンド)のオプションの詳細については
http://java.sun.com/j2se/1.5.0/ja/docs/ja/tooldocs/windows/java.html
などを参照して下さい。)

このJVMのオプションをEclipse上で設定するために、以下の手順を実施して下さい。

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

(2) 「実行構成」ウインドウが開きますので「名前」欄がItemListActionTest
になっていることを確認しましょう。なっていない場合は、左側のリストの中
から「JUnit」の配下の「ItemListActionTest」を選択(クリック)します。

(3) 「引数」タブをクリックし、「VM引数」欄に下記を入力します。
-Dut="junit"

(4) 「閉じる」ボタンをクリックしてウインドウを閉じます。



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


さて、ItemListActionTestクラスのソース・コードは現在

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

import junit.framework.TestCase;

public class ItemListActionTest extends TestCase {

   public void testExecuteActionMappingActionFormHttpServletRequestHttpServletResponse() {
      fail("まだ実装されていません");
   }

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

というようになっているはずですが、これを

--------------------------------------------------------
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("C:\\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();
   }

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

というように書き換えてみて下さい。

ここで、MockStrutsTestCaseはStrutsTestCaseが提供するクラスで、JUnitのTestCase
のサブクラスになっています。
したがって、MockStrutsTestCaseではJUnitのTestCaseの機能が一通り使える上に
MockStrutsTestCase独自の機能も使えるということになります。

Mockには、模造品とか偽物(にせもの)という意味がありますが、StrutsTestCaseは
まさにStrutsの模造品的な(擬似的な)環境を提供するもので、JUnitの機能の他に
Actionクラスの実行に必要なものを擬似的に提供してくれます。
(ちなみに車のメーカーが新しい車を開発するときに、よく粘土などで実物大の模型
を作って車体のデザインを確認することがありますが、あの模型は英語ではmock-upと
呼びます。ITの世界でも、同様に模型のことをmock-up(モックアップ)と呼ぶこと
がありますので、ついでに覚えておくとよいでしょう。)

詳しく(上記のソース・コードの解説)は、次回お話いたします。


以上で、単体テストができるお膳立てが揃いました。


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


(次回に続く)



┏━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
★ホームページ:
      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. 不許無断複製