DBUnit をそのまま使うと ORACLE 相手のテストが遅い
表題通りである。ORACLE データベースを相手に DBUnit を使用すると、とにかく遅い。
ただ、これには理由があり、解消方法も存在するが、これに気がつくまでかなり時間をかけてしまったので、残しておくことにする。
まず解消方法から。以下のように、テストデータを挿入するテーブルのスキーマを指定するだけで良い。(コードは Spring Boot で使用している場合の Bean 定義より抜粋)
// 中略 @Bean @Autowired public DataSourceDatabaseTester tester(DataSource dataSource, @Value("${spring.datasource.schema}") String schema) throws Exception { DataSourceDatabaseTester tester = new DataSourceDatabaseTester(dataSource); tester.setSchema(schema); tester.setSetUpOperation(DatabaseOperation.CLEAN_INSERT); return tester; } // 中略
これ、何が起きているかを説明するには、はじめに DBUnit がテストデータの挿入前に、何をしているかを知る必要がある。
- まず、DBUnit はテストデータを挿入する前に、与えられたテストデータを読み取る。ここまでは誰でも予想がつく動作だろう。
- つぎに、DBUnit はテストデータから得られた、ターゲットテーブル、ターゲットカラムが、対象 DB 内に存在するかどうかを確認する。
- このとき、対象 DB 内に該当するメタデータが存在しない場合、DBUnit は例外をスローして終了する。
- ここまでのチェックが完了して、はじめて
SetUpOperation
で指定された通りのテストデータ処理に入る。
以上が DBUnit のおこなう、テストデータに関する処理の流れなのだが、(3) で挙げた、対象 DB メタデータと、テストデータの照合の際に問題が生じるため、ORACLE 相手のテストで著しい遅延が発生する。(環境にもよるが手持ちの環境では、メタデータ取得だけで、10~20秒程度の待機が発生していた。)
問題とはどのようなものかというと、以下のようなものになる。
ORACLE データベースの JDBC ドライバでは、メタデータの取得時に、all_objects
というシステムテーブルに問い合わせをかける。このテーブルではログインしているユーザーがアクセス可能な、あらゆるオブジェクトの情報が返されるのだが、ここでスキーマ指定がないと、本来テスト対象とはならないスキーマも含めた問い合わせがかけられ、これが遅延につながってしまう。
all_objects
で返されるメタデータの中には、アクセス可能なだけで、ログイン中のユーザーでは操作自体は不可能な情報も多分に含まれているため、テストデータを用意するというオペレーションに対しては過大な情報といえる。そこで、ログインユーザーと同様のスキーマ情報を指定してやることで、ドライバ内で組み立てられるSQL で、all_objects
に対する WHERE 条件が追加され、遅延は大きく改善されるようになる。(遅延改善後の待機時間は、0~2秒程度。)
ORACLE 相手に DBUnit を使用したいと考えたとき、気になる遅延が発生した場合には、試してみてほしい。
また、参考までにこれらの挙動の詳細を確認したい場合は、JAD などを使用して oracle.jdbc.OracleDatabaseMetaData
の内容を確認してみるのも良いかと思う。