自分で作りたいものを作る時間を増やすとアウトプットする時間がなくなるというジレンマ。
年末年始の棚卸しのおかげでReactで作っているアプリケーションはだいぶ見通しが良くなった。ネタもたくさんあるんだけどもなかなか筆が重いというか時間が足りないというか。
さて、久々にJava。ちょっとしたヘルプで調べた内容の備忘録。
表題の通りで、DBUnitでPostgreSQLのUUIDを利用したテーブルのテストをするときにぶつかった事象について。
特に考慮がされていない場合、NoSuchColumnExceptionが発生する問題について。同じ事象自体はStackOverflowにあるのでそちらを参照。解決策がまとまっている訳ではないので、それを簡単にまとめておきます。
特に何も意識せずにDBUnitを使っていると、DefaultDataTypeFactoryというものが自動的に割り当てられているはずです。しかし、PostgreSQLのUUIDのように、DBMSに依存した型を使っている場合はこれでは解決できないため、DBMSごとのDataTypeFactoryを適用する必要があります。
今回だと、PostgreSQLなので、PostgresqlDataTypeFactoryを利用します。
参考:http://dbunit.sourceforge.net/faq.html#typefactory
さて、これを適用するためには、上記リンクのようにIDatabaseConnectionの実装クラスのインスタンスに設定をする必要があるわけなのですが、IDatabaseTesterの実装クラスを用いている場合は少々工夫をする必要があります。
今回使っていたのはDataSourceDatabaseTesterでしたので、その実装を見てみます。
コメントは省略しています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
package org.dbunit; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.sql.DataSource; import org.dbunit.database.DatabaseConnection; import org.dbunit.database.IDatabaseConnection; public class DataSourceDatabaseTester extends AbstractDatabaseTester { private static final Logger logger = LoggerFactory.getLogger(DataSourceDatabaseTester.class); private DataSource dataSource; public DataSourceDatabaseTester( DataSource dataSource ) { super(); if (dataSource == null) { throw new NullPointerException( "The parameter 'dataSource' must not be null"); } this.dataSource = dataSource; } public DataSourceDatabaseTester(DataSource dataSource, String schema) { super(schema); if (dataSource == null) { throw new NullPointerException( "The parameter 'dataSource' must not be null"); } this.dataSource = dataSource; } public IDatabaseConnection getConnection() throws Exception { logger.debug("getConnection() - start"); assertTrue( "DataSource is not set", dataSource!=null ); return new DatabaseConnection( dataSource.getConnection(), getSchema() ); } } |
ハイライトした40-46行目がポイントです。
他のIDatabaseTesterの実装クラスも大体同様だと思いますが、getConnectionを呼び出すと、DatabaseConnectionのインスタンスを新たに生成して返却しています。
DatasourceDatabaseTesterを利用するコードのかなりざっくりとしたイメージとしては、こんな感じになると思います。
1 2 3 4 5 6 7 8 9 10 |
protected IDatabaseTester tester; public void createTester(DataSource dataSource) { tester = new DataSourceDatabaseTester(dataSource); // なんやかんや設定 tester.onSetup(); } |
処理を追うと分かりますが、onSetupなどを呼び出すと、その先の処理でgetConnectionが呼び出される実装になっています。DataTypeFactoryは、getConnectionの戻り値であるIDatabaseConnectionのインスタンスに設定してあげる必要がありますが、getConnectionからは毎回新しいインスタンスが返却されるので、DataSourceDatabaseTesterのインスタンスからConfigを設定しても意味がなく、設定を効かせるためにはgetConnectionを拡張する必要があります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
protected IDatabaseTester tester; public void createTester(DataSource dataSource) { // ダメな例 // tester = new DataSourceDatabaseTester(dataSource); // tester.getConnection().getConfig() // .setProperty(DatabaseConfig.PROPERTY_DATATYPE_FACTORY, new PostgresqlDataTypeFactory()); // getConnectionを拡張する tester = new DataSourceDatabaseTester(dataSource) { @Override public IDatabaseConnection getConnection() throws Exception { IDatabaseConnection connection = new DatabaseConnection(dataSource.getConnection(), getSchema()); connection.getConfig() .setProperty(DatabaseConfig.PROPERTY_DATATYPE_FACTORY, new PostgresqlDataTypeFactory()); return connection; } } // なんやかんや設定 tester.onSetup(); } |
ということで、数行の拡張でDataTypeFactoryを適応することができますね。
直接Connectionのインスタンスに触ることができるのであれば、そのまま設定すれば良いのでもっと簡単ですね。
今回は既存との関係で適応が難しかったので詳しく調べていませんが、Springを使っている場合だとまた別の設定方法があるのかもしれません。
追記:Twitterで@shiroikaさんに教えていただきましたが、この辺りのようです。
まさに後者の方は上記の拡張にたどり着く前に試していたやつでした。なるほどやはりそうなのね。
- https://springtestdbunit.github.io/spring-test-dbunit/
- https://github.com/springtestdbunit/spring-test-dbunit/issues/67
今回の例はPostgreSQLに閉じた拡張ですが、利用するDBに合わせて設定をできるようにテスト基盤を作っておくと良さそうですね。
これくらいのボリュームなら30分そこらで書けるから、軽いネタを作っていきたい。