2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Spring Test DBUnitでテストする時、テスト用スキーマに切り替える

Last updated at Posted at 2022-05-06

はじめに

以前の記事で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;
	}

}

参考文献

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?