こんにちは、中村です。
DbUnit+JNDIUnitTestHelperで単体試験を実施する機会がありましたので、紹介したいと思います。
DbUnitとは、DB処理の単体試験を実施するためのフレームワークです。
※今回は、DbUnitの「テスト実施前のDBの保持」、「一時的なテストデータの挿入」機能のみを紹介します。DbUnitには、紹介する機能の他にDBデータをオブジェクト化し、比較する機能など多くの機能を持っていますが、これらの機能については触れません。
また、今回使用するDbUnitのバージョンは2.4.7です。
2.4.8で使い方が異なるクラスがあるようですので、バージョンに注意してください。
加えて今回はJNDIUnitTestHelperを利用してDBアクセスを行います。
JNDIUnitTestHelperを利用することで、DBへの接続情報(データソース)を外部ファイルから取得することができるため、DBアクセスにAPサーバに設定されたデータソースを参照している場合、APサーバにアクセスすることなくDB処理の単体試験を実施することができます。
○テスト実行の流れ
テスト実行の流れの流れは以下の通りです。
- テスト実行前DBデータの退避(一時ファイル保存)[@BeforeClass]
- テストデータ(xmlファイル)の挿入[@Before]
- テスト実行[@Test]
- 2~3を@Testの数だけ繰り返し
- 退避していたテスト実行前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の機能の一部しか利用していませんが、それだけでも十分利用価値があると思います。
もし機会があればぜひ使ってみてください。