Oracle + JUnit4 で DBTestCaseを継承せずに dbUnit を使う

Oracle + JUnit4 で DBTestCaseを継承せずに dbUnit 使おうと思ったら、ちょっと苦労したのでメモ。

http://www.dbunit.org/howto.html#noextend によると、DBTestCase を継承しないで DBUnit のテストケースを書くには、dbUnit 2.2以降はIDatabaseTester が使えるようである。そこで、サンプルをもとに、以下の様に書いてみた。

public class HogeTest {

        private static IDatabaseTester dbTester;
       
        @BeforeClass
        public static void setUpBeforeClass() throws Exception {
              
               dbTester = new JdbcDatabaseTester("oracle.jdbc.driver.OracleDriver" ,"jdbc:oracle:thin:@xxxx:1521:xxx" ,"user" ,"pass");
               dbTester.setSetUpOperation(DatabaseOperation.INSERT);
               dbTester.setTearDownOperation(DatabaseOperation.DELETE);
              IDataSet dataset = new FlatXmlDataSetBuilder().build(new FileInputStream("testdata/hogeDataset.xml" ));
               dbTester.setDataSet(dataset);
       }

        @AfterClass
        public static void tearDownAfterClass() throws Exception {
       }

        @Before
        public void setUp() throws Exception {
               dbTester.onSetup();
       }

        @After
        public void tearDown() throws Exception {
               dbTester.onTearDown();
       }

        @Test
        public void testMethod() {
               // test
       }

}

これでテストを実行すると、以下の様なエラーが発生する。

AmbiguousTableNameException

これは、http://www.dbunit.org/faq.html#AmbiguousTableNameException に記載があった。
スキーマ名を指定せよとのこと。とくに、Oracle の場合は、スキーマ名を大文字でしてする必要があるらしい。
以下の様に書き換える。

dbTester = new JdbcDatabaseTester("oracle.jdbc.driver.OracleDriver" ,"jdbc:oracle:thin:@xxxx:1521:xxx" ,"user" ,"pass","SCHEMA");

これで、AmbiguousTableNameException は発生しなくなった。
しかし、今度は、dbTester.onSetup() 、テストデータの登録時に以下の様な例外が発生するようになった。

org.dbunit.dataset.NoSuchColumnException:

dataset の xml に誤りは無く、Column名が間違っていたわけではない。
ぐぐってみると、dataTypeFactory とやらを Oracle 用にしてみれば良いらしい。そこで、以下の記述を追加する。

dbTester.getConnection().getConfig().setProperty(DatabaseConfig. PROPERTY_DATATYPE_FACTORY, new OracleDataTypeFactory());

しかし、これでは例外が解決しなかった。
考えたあぐねて、デバッグモードでdbTester.getConnection().getConfig() のプロパティを見てみたら、DefaultDataTypeFactory のままだった。

なぜ設定が反映されないのか。さっぱりわからなくなって、 JdbcDatabaseTester#getConnection のソース読んだら以下の様になっていた。

public IDatabaseConnection getConnection() throws Exception {
        logger.debug("getConnection() - start");
        assertNotNullNorEmpty( "connectionUrl", connectionUrl );
        Connection conn = null;
        if( username == null && password == null ){
            conn = DriverManager.getConnection( connectionUrl );
        }else{
            conn = DriverManager.getConnection( connectionUrl, username, password );
        }
        return new DatabaseConnection( conn, getSchema() );
    }

最後でDatabaseConnectionをnewしてやがる…
つまり、getConnectionでは常に新しいIDatabaseConnectionが返るので、せっかくプロパティをセットしても保持してくれないのだ。

ちなみに、他のIDatabaseTesterの実装も同様の実装だった。

しょうが無いので、JdbcDatabaseTesterを継承し、getConnectionをオーバーライドしてその都度プロパティをセットするようにした。

最終的には、以下の様なコードになった。

public class HogeTest {

        private static IDatabaseTester dbTester;
       
        @BeforeClass
        public static void setUpBeforeClass() throws Exception {
              
               dbTester = new JdbcDatabaseTester("oracle.jdbc.driver.OracleDriver" ,"jdbc:oracle:thin:@xxxx:1521:xxx" ,"user" ,"pass","SCHEMA") {
                      @Override
                      public IDatabaseConnection getConnection() throws Exception {
                           IDatabaseConnection conn = super.getConnection();
                            conn.getConfig().setProperty(DatabaseConfig. PROPERTY_DATATYPE_FACTORY, new OracleDataTypeFactory());
                            return conn;
                     }
               };
               dbTester.setSetUpOperation(DatabaseOperation.INSERT);
               dbTester.setTearDownOperation(DatabaseOperation.DELETE);
              IDataSet dataset = new FlatXmlDataSetBuilder().build(new FileInputStream("testdata/hogeDataset.xml" ));
               dbTester.setDataSet(dataset);
       }

        @AfterClass
        public static void tearDownAfterClass() throws Exception {
       }

        @Before
        public void setUp() throws Exception {
               dbTester.onSetup();
       }

        @After
        public void tearDown() throws Exception {
               dbTester.onTearDown();
       }

        @Test
        public void testMethod() {
               // test
       }

}

これでようやく動作した。