kuniku’s diary

はてなダイアリーから移行(旧 d.hatena.ne.jp/kuniku/)、表示がおかしな箇所はコメントをお願いします。記載されている内容は日付およびバージョンに注意してください。直近1年以上前は古い情報の可能性が高くなります。

SpringとiBatisでのTestCase

DIコンテナであるSpringを利用したWebアプリケーションに手を加える
かもしれないとのことで、テストってどうやるのかなと思い試してみた。
ビジネスロジック的に訳のわからないプログラムに手を出すことをしたことがないので試行錯誤しなきゃならん、Spring使ったことないしなと思って)

もともとのプロジェクトファイル(実行するには必要なファイル)を取得したんだけど
テストクラスない

ぅぅぅ・・・SQLくらい簡単に実行できんと厳しいとちゃうんか?
ORマッピングとしてiBatis使ってるんだから、
コンテナに依存せずにサクっとSELECTとかしてみたいじゃん?

どうやるのかな・・・、そのあたりのSpringやiBatisのドキュメントってあるのか?
あるんだろうけど、Englishだよな><。

さっそくぐぐってみた。
参考になりそうなのがいくつかあった。

上記を参考にして

テストクラスの作成

Daoインターフェイス、Daoインターフェイスの実装クラス、iBatisでのsqlMapファイル、SpringのapplicationContext*.xml
すでにあるという前提で試します。
(Springのバージョンは1.2想定)

Eclipseでの設定

1)Eclipse3.3を使ってるのだけど、当該プロジェクトでjunitを使用できるようにする
2)当該プロジェクト右クリックのプロパティ→ライブラリ→JUnit→JUnit3を選択→終了
3)テスト用のソースフォルダを作成
  当該プロジェクト右クリック→新規→ソース・フォルダー →
   フォルダー名:test
   →終了

4)テストクラスを作成するパッケージを作成し、そのパッケージを選択して右クリック→新規→JAVAJUnitJUnitテスト・ケース→
  (新規→JUnitテスト・ケースでも同じ)
 ・名前を入力 名前=Daoインターフェイスの実装クラス+Test
 ・setUp()にチェックを入れる
 ・テスト元クラスを選択(Daoインターフェイスの実装クラス) 別に選択(入力)しなくてもよいけど。

4)自動生成されたクラスに対して、以下のように実装

import文割愛

public class CsvDaoImplTest extends TestCase {

  /** <code>CsvDaoImpl</code> test対象のインターフェイス */
  private CsvDao dao = null;
  
  /** <code>CONTEXT_PATH</code> xmlファイルの指定 */
  private static final String CONTEXT_PATH ="/WEB-INF/conf/spring/applicationContext*.xml";//・・・(A)
  
  
  /* (非 Javadoc)
   * @see junit.framework.TestCase#setUp()
   */
  @Override
  protected void setUp() throws Exception {
    //このテストで先に実行しておくべき事項をこのsetUpメソッドで実装する
    super.setUp();
    
    //アプリケーションコンテキストを取得
    //Seasarでいうところのコンテナ初期化みたいな・・・。
    ApplicationContext ctx = new ClassPathXmlApplicationContext(CONTEXT_PATH);//・・・(B)
    
    //取得するBean(コンポーネント)の名称
    final String beanId4DaoName ="mailFindForCsvDao";
    
    //自分でDaoインターフェイスのインスタンスにインジェクションする コンポーネントの取得とDI
    //Springのコンテナからインターフェイスの型にキャストして取り出す。
    dao = (CsvDao) ctx.getBean(beanId4DaoName);//・・・(C)
    
  }

  /**
   * テスト対象のメソッド 
   **/
  public void testfindMailForCsv() throws Exception {

    //daoのメソッドを実行
    List rsList = dao.findMailForCsv();//・・・(D)
    
    //取得した結果をループ
    for (int i = 0; i < rsList.size(); i++) {
      
      MailDomain mailInfo = (MailDomain) rsList.get(i);
      
      //取得したものをsysout
      System.out.println(mailInfo.getShopCd());
      System.out.println(mailInfo.getEmail());
      
    }
    
    assertNotSame("リストが0件だお。おかしくない?",0, rsList.size());
    System.out.println("抽出したリスト数="+rsList.size());
    
  }
}

A:Springの設定情報を記述したファイルを指定する
B:setUpメソッド内にて、Springのコンテナを生成する
C:テスト対象のdaoをSpringのBeanのidを指定して取り出す。インターフェイスの型にしてキャストして取り出す
D:テスト対象メソッドを記述

これで、JUnitを実行

でも、これだとclassパス上に存在しないというエラーが出る。Aで指定したファイルが取得できないってね。

こんな感じのエラー

org.springframework.beans.factory.BeanDefinitionStoreException: Could not resolve bean definition resource pattern [/WEB-INF/conf/spring/applicationContext*.xml]; nested exception is java.io.FileNotFoundException: class path resource [WEB-INF/conf/spring/] cannot be resolved to URL because it does not exist
java.io.FileNotFoundException: class path resource [WEB-INF/conf/spring/] cannot be resolved to URL because it does not exist
	at org.springframework.core.io.ClassPathResource.getURL(ClassPathResource.java:155)
・
・

http://d.hatena.ne.jp/getbean/20071219

テストケースの作成
5.雛形を次の内容で修正し、JUnitでテストを実行する。この際、実行時のクラスパスにプロジェクト配下の「WebContent」を追加する必要がある。

とあるので、クラスパスに追加しないとならんのかってことはわかったけど
Eclipse上でどうすればよいのか不明。
下記の2つの方法で、とりあえずは動作はできる。でも正しいのかわからない。
・プロジェクト右クリック→プロパティ→Javaのビルド・パス→ライブラリーのタブを選択
 →クラスフォルダーの追加 押下→プロジェクト配下のWebContenにチェックを入れ→OK

・プロジェクト右クリック→プロパティ→Javaのビルド・パス→ソースのタブを選択
 →フォルダーの追加→WebContenを選択→OK

で動作はできた。

補足

今回のWebアプリケーションではSpringとiBatisの連携でのデータソース関連の
設定をしているのでそのファイル(applicationContext-xxxx.xml)を変更。

datasource.propertiesというファイル内の定義でdataSourceの設定情報を保持しているので、
そのファイルをテスト時にも読み込ませないといけない。
そのpropertyファイルの取得パスがWebアプリ実行時のパスになっているので、単体テストでは
そのパスは取得できない。
ってことでその対策としてスマートじゃないけど
testフォルダーを追加したので、そのフォルダー配下にWebアプリ実行時
と同じファイル(datasource.properties)を置いた。

testフォルダーはソースフォルダーとして認識させているので、クラスパスが通り
そのpropertyファイルが取得できる。

applicationContext-xxxx.xmlは以下のような内容

<beans>
  <!-- Properties File Path -->
  <bean id="propertyConfigurer"
    class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="locations">
      <list>
        <!-- webアプリ実行時の場合  -->
        <!-- 
        <value>/WEB-INF/classes/resources/datasource.properties</value>
        -->
        <!-- ユニットテスト用 -->
        <value>datasource.properties</value>
      </list>
    </property>
  </bean>
  
  <!-- DataSource Settings -->
  <bean id="dataSource"
    class="oracle.jdbc.pool.OracleDataSource" destroy-method="close">
    <property name="URL">
      <value>${jdbc.oracle.url}</value>
    </property>
    <property name="user">
      <value>${jdbc.oracle.username}</value>
    </property>
    <property name="password">
      <value>${jdbc.oracle.password}</value>
    </property>
    <property name="connectionCachingEnabled">
      <value>${jdbc.oracle.connectionCachingEnabled}</value>
    </property>
    <property name="connectionCacheProperties">
      <props>
        <prop key="MaxLimit">${jdbc.oracle.maxLimit}</prop>
        <prop key="InitialLimit">${jdbc.oracle.initialLimit}</prop>
        <prop key="ConnectionWaitTimeout">${jdbc.oracle.connectionWaitTimeout}</prop>
        <prop key="ValidateConnection">${jdbc.oracle.validateConnection}</prop>
      </props>
    </property>
  </bean>
・
・
・
</beans>