DbUnit + JNDIUnitTestHelperで単体試験

こんにちは、中村です。

DbUnit+JNDIUnitTestHelperで単体試験を実施する機会がありましたので、紹介したいと思います。

DbUnitとは、DB処理の単体試験を実施するためのフレームワークです。
※今回は、DbUnitの「テスト実施前のDBの保持」、「一時的なテストデータの挿入」機能のみを紹介します。DbUnitには、紹介する機能の他にDBデータをオブジェクト化し、比較する機能など多くの機能を持っていますが、これらの機能については触れません。

また、今回使用するDbUnitのバージョンは2.4.7です。
2.4.8で使い方が異なるクラスがあるようですので、バージョンに注意してください。

加えて今回はJNDIUnitTestHelperを利用してDBアクセスを行います。
JNDIUnitTestHelperを利用することで、DBへの接続情報(データソース)を外部ファイルから取得することができるため、DBアクセスにAPサーバに設定されたデータソースを参照している場合、APサーバにアクセスすることなくDB処理の単体試験を実施することができます。

○テスト実行の流れ

テスト実行の流れの流れは以下の通りです。

  1. テスト実行前DBデータの退避(一時ファイル保存)[@BeforeClass]
  2. テストデータ(xmlファイル)の挿入[@Before]
  3. テスト実行[@Test]
  4. 2~3を@Testの数だけ繰り返し
  5. 退避していたテスト実行前DBデータの復帰[@AfterClass]

○ テスト実行前DBデータの退避

テスト実行前DBデータの退避は@BeforeClassアノテーションを付与したメソッド内で行います。

private static IDatabaseTester databaseTester;
private static File tmpfile;
@BeforeClass
public static void setUpBeforeClass() throws Exception {
    // JNDIUnitTestHelperの初期化
    JNDIUnitTestHelper.init("jndi_unit_test_helper.properties");
    
    // IDatabaseTesterに接続情報を設定します。
    databaseTester = new JndiDatabaseTester(null, "jdbc/DS", "schema");

    // DBのデータタイプを設定します。(今回はOracle DB)
    databaseTester.getConnection().getConfig().setProperty(
        DatabaseConfig.PROPERTY_DATATYPE_FACTORY, new OracleDataTypeFactory());
    
    // データを退避させるテーブル名をを配列に格納します。
    String[] table = {"Table01", "Table02", "Table03"};

    // DBデータをdb_tmp.xmlファイルに書き出します。
    final IDataSet dataSet = databaseTester.getConnection().createDataSet(table);
    tmpfile = File.createTempFile("db_tmp", "xml");
    FlatXmlDataSet.write(dataSet, new FileOutputStream(tmpfile));
}

DBへのアクセスはIDatabaseTesterクラスが行なっており、JNDIUnitTestHelperクラスを利用することで”jndi_unit_test_helper.properties”に定義されたデータソースで初期化しています。

jndi_unit_test_helper.properties設定例

com.javaranch.unittest.helper.sql.pool.JNDIName=jdbc/DS
com.javaranch.unittest.helper.sql.pool.dbDriver=oracle.jdbc.driver.OracleDriver
com.javaranch.unittest.helper.sql.pool.dbServer=jdbc:oracle:thin:@DataBaseServer:1521:xe
com.javaranch.unittest.helper.sql.pool.dbLogin=db
com.javaranch.unittest.helper.sql.pool.dbPassword=!DBPassword123

データタイプの設定で今回はOracleDBを利用しましたが、PostgreSQLの場合は「OracleDataTypeFactory」を「PostgresqlDataTypeFactory」に書き換えれば良いです。

○ テストデータ(xmlファイル)の挿入

テストデータの挿入は@Beforeアノテーションを付与したメソッド内で行います。

@Before
public void setUp() throws Exception {
    // 事前テストデータをデータベースに格納します。
    IDataSet dataSet = new FlatXmlDataSetBuilder().build(
        new FileInputStream("TestData.xml"));
    databaseTester.setDataSet(dataSet);
    databaseTester.onSetup();
}

上記のように、TestData.xmlに記述されたテストデータを読み込み、IDatabaseTester#onSetupメソッドで挿入しています。

読み込むxmlファイルは以下のように記述します。

<dataset>
  <TABLE_NAME COLUMN1_NAME="COLUMN1_VALUE" COLUMN2_NAME="COLUMN2_VALUE" COLUMN3_NAME="COLUMN3_VALUE" />
  <TABLE_NAME COLUMN3_NAME="COLUMN3_VALUE" />
  <EMPTY_TABLE />
</dataset>

外部キー制約がある場合、以下の点に注意しなければなりません。

  • 親テーブルを子テーブルよりも上に記述する。
  • 親テーブルに対して存在しうる子テーブル全てを記述する。
    (使用しないテーブルの場合、上記EMPTY_TABLEのように記述する)

というのも、IDatabaseTester#onSetupメソッドではxmlに記述された全テーブルのDelete/Insertを行なっており、かつ処理順番が下からのため、親テーブルを参照する子テーブルが残っている(親テーブルよりも上に子テーブルが記述されている or 参照している子テーブルがxmlに記述されていない)場合、親テーブルのDeleteができないのです。

以上を踏まえた外部キー制約がある場合の記述例

<dataset>
  <PARENT_TABLE COLUMN1_NAME="COLUMN1_VALUE" COLUMN2_NAME="COLUMN2_VALUE" COLUMN3_NAME="COLUMN3_VALUE" />
  <CHILD_TABLE1 COLUMN3_NAME="COLUMN3_VALUE" />
  <CHILD_TABLE2 />
</dataset>

○ 退避していたテスト実行前DBデータの復帰

退避していたテスト実行前DBデータの復帰は@AfterClassアノテーションを付与したメソッド内で行います。

@AfterClass
public static void tearDownAfterClass() throws Exception {
    // 保存しておいたデータをデータベースに格納し、テスト前の状態に戻します。
    final IDataSet dataSet =
        new FlatXmlDataSetBuilder().build(new FileInputStream(tmpfile));
    databaseTester.setDataSet(dataSet);
    databaseTester.setTearDownOperation(DatabaseOperation.CLEAN_INSERT);
    databaseTester.onTearDown();
}

上記のように、テスト開始時に保存したファイルを再読み込みし、DBをテスト開始時の状態に復帰しています。

以上で紹介を終わります。
DbUnitの機能の一部しか利用していませんが、それだけでも十分利用価値があると思います。
もし機会があればぜひ使ってみてください。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です