こんにちは、中村です。
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の機能の一部しか利用していませんが、それだけでも十分利用価値があると思います。
もし機会があればぜひ使ってみてください。