2008-06-08

Springベースのユニットテストに DbUnitを組み合わせる方法

DbUnitは、JUnitにデータベース入出力のテストを行うための各種便利機能を提供するための拡張である。
ここでは、フィクスチャのロードを行う機能にスコープを絞って DbUnitを取り上げる。
フィクスチャとは、ユニットテストの事前条件となるテストデータである。

DbUnitを使うには、クラスパスに下記のjarファイルを追加する。

dbunit-*.jar
slf4j-api-*.jar
slf4j-jcl-*.jar

フィクスチャを使うユニットテストは、各テストメソッド毎に下記のような流れで実行される。

a) トランザクション開始

b) フィクスチャをデータベースにINSERT

c) テスト対象メソッドの呼び出し

d) 実行結果のチェック

e) トランザクションをロールバック

a, e は Springの機能で自動的に行われる。
c, d はテストケースの実装者がコーディングするとして、残る b を DbUnitで行うということになる。

テストケースの各トランザクションを常にロールバックする設定にしておけば、データベースは各テストメソッドが終了するたびにテストを開始する前の状態へ戻るため、INSERTされたフィクスチャも取り消される。そのため、固定のテストデータを用いても2回目以降のテストで重複キーの問題が発生するということは無い。

前回のエントリSpringとユニットテストで解説したような、Springによって自動的にトランザクション管理が行われるように構成された、つまり

@RunWith(SpringJUnit4ClassRunner.class)
@TransactionConfiguration
@Transactional
@ContextConfiguration

といったアノテーションの付加されたテストケースをさらに DbUnitへ対応させるには、テストケースに下記のような追加を行う。

1) DataSourceBasedDBTestCaseを継承させる
2) DataSource型のプロパティを持たせ、Springでインジェクトさせる
3) protected DataSource getDataSource()メソッドを実装する (単に 2のデータソースオブジェクトを返すだけで良い)。このメソッドはDbUnitが利用する。
4) @Beforeアノテーションを付けて、public void setUp()を実装する。単に super.setUp()をコールするだけで良い。
5) protected IDataSet getDataSet()をオーバーライドする。ここには、フィクスチャとなるデータセットオブジェクトを返す処理を記述する。このメソッドはDbUnitが利用する。

注意しておきたいのは 2 のデータソースである。Springと DbUnitを一緒に使うためのデータソースは TransactionAwareDataSourceProxyでなくてはならない
DbUnitはデータソースから取得した物理コネクションをフィクスチャのINSERTが完了した後に一度クローズしてしまう。Springのトランザクション管理下ではこれは不正な操作であり(DbUnitは Springの事など知らないのでやむを得ないのだが)、このため素のデータソースを DbUnitへ渡すとテスト本体が正常に実行できなかったりする。
Springの TransactionAwareDataSourceProxyでデータソースを包んでおけば、物理コネクションが適切な Proxyオブジェクトによって保護されるため、この問題を防ぐことができる。

デフォルトでは、DbUnitはフィクスチャをデータベースに INSERTする際にテーブルを一旦クリアしてしまう。もし接続先のデータベースがユニットテスト専用ならばこの動作は妥当だが、そうでない場合(同じデータベースを結合テストにも使用するなど)はテーブルをクリアしてほしくないだろう。ユニットテスト実行時にテーブルをクリアせずフィクスチャの INSERTだけを行うようにするには、protected DatabaseOperation getSetUpOperation() メソッドをオーバーライドして DatabaseOperation.INSERT を返すようにすると良い。
(但し、できれば結合テスト用のデータベースとユニットテスト用のデータベースは別々に設けたいところである。例えば Ruby on Railsではそのような習わしになっている)

フィクスチャは Excelで記述することが出来る(私が DbUnitを使いたいと思った唯一の理由がこれだ)。
Excelの各シートがデータベースのテーブルに対応する。シート名としてテーブル名を用いることによって、シートがテーブルと対応づけられる。
各シートの最上行にはカラム名を記載し、2行目以降に INSERTしたいデータを並べる。このようにして作成した Excelファイルを fixtures.xls として保存したなら、先の 5番にある protected IDataSet getDataSet() の実装はこのようになるだろう。

@Override
protected IDataSet getDataSet() throws Exception {
return new XlsDataSet(new File("fixtures.xls"));
}

ラベル:

0 件のコメント:

コメントを投稿

<< ホーム