はじめに
以前の記事でDBUnitを使ったテストを実装しました。
今回はテストする時に専用のスキーマ(MySQLの場合データベース)に切り替えるようにします。
実行環境
OS:Mac
Spring Test DBUnit:spring-test-dbunit 1.3.0
DBUnit:DBUnit 2.7.3
JUnit:JUnit5.8.2
Java:Amazon Corretto 8
Spring Boot:2.6.1
Gradle:gradle6.8
IDE:intellij idea community edition 2021.1
DB:MySQL8.0
テスト用のDBを作成
まず、テストする時に使用するDBを作成します。
テスト用に「helloworldTest」というDBを作成し、
空っぽの「hello_world」テーブルを作成します。
CREATE DATABASE helloworldTest;
USE helloworldTest;
create table hello_world (
id VARCHAR(10) default '' not null
, name VARCHAR(50) default '' not null
, unique hello_world_index (id)
) ;
テスト用にデータソースの設定する
テスト用パッケージにデータソースのBeanを実装します。
以下のようにDataSourceオブジェクトを生成し、
DataSourceにはテスト用に用意した「helloworldTest」を指定します。
helloworld/
└ src/
└ main/
│
└ test/
└ java/
└ パッケージ/
└ config/
└ DatasourceConfig.java
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy;
import javax.sql.DataSource;
@Configuration
public class DatasourceConfig {
@Bean
public DataSource dataSource() {
return new TransactionAwareDataSourceProxy(
DataSourceBuilder
.create()
.username("root")
.password("my-secret-pw")
.url("jdbc:mysql://localhost/helloworldTest")
.driverClassName("com.mysql.cj.jdbc.Driver")
.build());
}
}
専用DBでテスト開始(失敗)
DataSourceでDBの繋ぎ先を切り替えたので、テスト実行!
以下のExceptionがthrowされてしまいました。
org.dbunit.database.AmbiguousTableNameException: HELLO_WORLD
at org.dbunit.dataset.OrderedTableNameMap.add(OrderedTableNameMap.java:198) ~[dbunit-2.7.3.jar:na]
at org.dbunit.database.DatabaseDataSet.initialize(DatabaseDataSet.java:244) ~[dbunit-2.7.3.jar:na]
at org.dbunit.database.DatabaseDataSet.getTableMetaData(DatabaseDataSet.java:298) ~[dbunit-2.7.3.jar:na]
at org.dbunit.operation.DeleteAllOperation.execute(DeleteAllOperation.java:109) ~[dbunit-2.7.3.jar:na]
at org.dbunit.operation.CompositeOperation.execute(CompositeOperation.java:79) ~[dbunit-2.7.3.jar:na]
at com.github.springtestdbunit.DbUnitRunner.setupOrTeardown(DbUnitRunner.java:183) ~[spring-test-dbunit-1.3.0.jar:na]
at com.github.springtestdbunit.DbUnitRunner.beforeTestMethod(DbUnitRunner.java:75) ~[spring-test-dbunit-1.3.0.jar:na]
at com.github.springtestdbunit.DbUnitTestExecutionListener.beforeTestMethod(DbUnitTestExecutionListener.java:185) ~[spring-test-dbunit-1.3.0.jar:na]
at org.springframework.test.context.TestContextManager.beforeTestMethod(TestContextManager.java:293) ~[spring-test-5.3.13.jar:5.3.13]
at org.springframework.test.context.junit.jupiter.SpringExtension.beforeEach(SpringExtension.java:174) ~[spring-test-5.3.13.jar:5.3.13]
// ・・・
AmbiguousTableNameException
こちらのページで調べて見ると、
同じ名前のテーブルが(DBを跨いでも)複数存在するとスローされるようです。
解決策として、以下の3つが挙げられていました。
1)1つのデータベーススキーマにのみアクセスできるデータベース接続クレデンシャルを使用する。
2)DatabaseConnectionまたは DatabaseDataSourceConnectionコンストラクターにスキーマ名を指定する。
3)修飾テーブル名のサポートを有効にする。
結果として2)、3)を実施することで解決出来ました。
問題の部分1
スタックトレースより、DatabaseDataSetクラスでテーブル名を取得している箇所を発見しました。
以下の207行目より、metadataHandlerにdatabaseMetaDataやschemaを指定してテーブルを取得していました。
しかし、デバッグするとschemaがnullだったため、上手く指定出来ていなかったことがわかります。
204 String[] tableType = (String[])config.getProperty(DatabaseConfig.PROPERTY_TABLE_TYPE);
205 IMetadataHandler metadataHandler = (IMetadataHandler) config.getProperty(DatabaseConfig.PROPERTY_METADATA_HANDLER);
206
207 ResultSet resultSet = metadataHandler.getTables(databaseMetaData, schema, tableType);
なので、DatabaseDataSourceConnectionにDB(スキーマ)を指定します。
@Bean
public IDatabaseConnection dbUnitDatabaseConnection(DataSource dataSource) throws SQLException {
return new DatabaseDataSourceConnection(dataSource, "helloworldTest");
}
問題の部分2
スキーマを指定したが、まだ改善しませんでした。
どうやら、DatabaseDataSetクラスの205行目でmetadataHandlerからスキーマを取得するのに失敗していたようです。
220 _schemaSet.add(schema);
221 while (resultSet.next())
222 {
223 String schemaName = metadataHandler.getSchema(resultSet);
224 String tableName = resultSet.getString(3);
なのでmetadataHandlerを調べて見るとドキュメントに記載がありました。
データベースメタデータ関連のメソッドを制御するために使用されるハンドラーを構成するために使用されます。
オブジェクトは org.dbunit.database.IMetadataHandlerを実装する必要があります。
とのこと
もう一度、DatabaseDataSourceConnectionを修正します。
MySqlMetadataHandlerでIMetadataHandlerを実装します。
@Bean
public IDatabaseConnection dbUnitDatabaseConnection(DataSource dataSource) throws SQLException {
IDatabaseConnection con = new DatabaseDataSourceConnection(dataSource, "helloworldTest");
con.getConfig().setProperty(DatabaseConfig.PROPERTY_METADATA_HANDLER, new MySqlMetadataHandler());
return con;
}
専用DBでテスト開始(成功)
以上を踏まえてテストを実行すると、上手く動きました。
以下、最終的なDatasourceConfigです。
import org.dbunit.database.DatabaseConfig;
import org.dbunit.database.DatabaseDataSourceConnection;
import org.dbunit.database.IDatabaseConnection;
import org.dbunit.ext.mysql.MySqlMetadataHandler;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy;
import javax.sql.DataSource;
import java.sql.SQLException;
@Configuration
public class DatasourceConfig {
@Bean
public DataSource dataSource() {
return new TransactionAwareDataSourceProxy(
DataSourceBuilder
.create()
.username("root")
.password("my-secret-pw")
.url("jdbc:mysql://localhost/helloworldTest")
.driverClassName("com.mysql.cj.jdbc.Driver")
.build());
}
@Bean
public IDatabaseConnection dbUnitDatabaseConnection(DataSource dataSource) throws SQLException {
IDatabaseConnection con = new DatabaseDataSourceConnection(dataSource, "helloworldTest");
con.getConfig().setProperty(DatabaseConfig.PROPERTY_METADATA_HANDLER, new MySqlMetadataHandler());
return con;
}
}
参考文献