DBUnitとは、データベースに依存するクラスのテストを行うためのJUnit拡張フレームワークです。
メリットとしては、データベースアクセスを伴うプロダクトコードの単体テストを行うときに、条件となるテーブルのレコード状態をテストコードによってつくることができるため、テストの再現性が高くなります。
依存関係
pom.xmlに以下の依存関係を追加します。
<!-- https://mvnrepository.com/artifact/org.dbunit/dbunit -->
<dependency>
<groupId>org.dbunit</groupId>
<artifactId>dbunit</artifactId>
<version>2.7.3</version>
<scope>test</scope>
</dependency>
また、今回は、インプットファイルとしてExcelファイル(.xls、.xlsx)を使用します。
そのため、Apache poiも必要になります。
<!-- https://mvnrepository.com/artifact/org.apache.poi/poi -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>5.2.5</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>5.2.2</version>
<scope>test</scope>
</dependency>
Excelファイルの定義
テーブルに対する入力となるExcelファイルを作成します。
以下スクショのように記述します。
(A5-mk2からレコードをコピーして貼り付けると非常に楽です。)
Excelファイルは、src/test/resources配下に置きましょう。
ポイントとしては、
・ヘッダ行が必要です。
・nullにしたい場合は、セルを空にします。
・シート名は、テーブルの物理名にします。
・シートは複数可能です。ここがcsvファイルとの違いで、何テーブルでも処理することができます。
なお、外部キー制約のあるテーブルを更新対象にする場合は、親テーブルを左のシート、子テーブルを右のシートにしましょう。逆順だと、実行時にSQLExceptionになります。
テストクラス
・DatabaseConnectionコンストラクタの第2引数には、スキーマ名を指定します。
・execute(iconn, dataSet)メソッドが、実際にテーブルレコードを書き換える処理になります。
下の例では、DatabaseOperation.CLEAN_INSERTとしており、まずテーブルレコードを全削除した上で、Excelに記載されたデータを投入する挙動になります。
import static org.junit.jupiter.api.Assertions.fail;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.sql.Connection;
import javax.sql.DataSource;
import org.dbunit.database.DatabaseConnection;
import org.dbunit.database.IDatabaseConnection;
import org.dbunit.dataset.IDataSet;
import org.dbunit.dataset.excel.XlsDataSet;
import org.dbunit.operation.DatabaseOperation;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class ApplicationTests {
@Autowired
DataSource dataSource;
public void cleanAndInsertTables(){
try (Connection conn = dataSource.getConnection()){
IDatabaseConnection iconn = new DatabaseConnection(conn, "public");
Path xlsxFile = Paths.get(
"src\\test\\resources\\table_data.xlsx");
IDataSet dataSet = new XlsDataSet(xlsxFile.toFile());
DatabaseOperation.CLEAN_INSERT.execute(iconn, dataSet);
}catch(Exception e){
e.printStackTrace();
fail();
}
}
@Test
public void testCase1(){
cleanAndInsertTables();
// do something
}
}
テストコードを実行すると、テーブルにはExcelで記述した7レコードのみが入った状態をつくることができます。
DatabaseOperationについて
DatabaseOperationには、以下の種類が用意されています(すべての挙動までは把握、確認していません)。
NONE
UPDATE
INSERT
REFRESH
DELETE
DELETE_ALL
TRUNCATE_TABLE
CLEAN_INSERT
TRANSACTION
CLOSE_CONNECTION
DELETEは、Excelに記載したレコードデータのみ削除します。
DELETE_ALLは、すべてのレコードを削除します。ただしTRUNCATE_TABLEの方が高速なので、基本的にこちらを使う方が良さそう。
(なお余談ですが、PostgreSQLではtruncateしてもシーケンスはリセットされません。MySQLではリセットされます)
最もよく使うのはCLEAN_INSERTでしょう。DELETE_ALLした後にINSERTする挙動になります。
補足
テストクラスで、データベースの接続情報として、
@Autowired
DataSource dataSource;
としていますが、これはapplication.propertiesに下記が定義されていれば、自動的にDIされるので、Configurationで定義する必要はありません。
spring.datasource.driver-class-name=org.postgresql.Driver
spring.datasource.url=jdbc:postgresql://アドレス:ポート/データベース名
spring.datasource.username=ユーザ名
spring.datasource.password=パスワード
下記のような記述は不要↓↓↓
import javax.sql.DataSource;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy;
@Configuration
public class DatasourceConfig {
@Bean
public DataSource dataSource() {
return new TransactionAwareDataSourceProxy(
DataSourceBuilder
.create()
.username("secret")
.password("secret")
.url("jdbc:postgresql://localhost:5432/sampledb")
.driverClassName("org.postgresql.Driver")
.build());
}
}