DBUnitを導入する（Spring Boot,Maven）

DBUnitとは、データベースに依存するクラスのテストを行うためのJUnit拡張フレームワークです。

メリットとしては、データベースアクセスを伴うプロダクトコードの単体テストを行うときに、条件となるテーブルのレコード状態をテストコードによってつくることができるため、テストの再現性が高くなります。

## 依存関係

pom.xmlに以下の依存関係を追加します。

```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も必要になります。

  ```xml
		<!-- 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からレコードをコピーして貼り付けると非常に楽です。）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/180727/ee9ade22-b383-f6a0-97e3-c4ad77dcdd02.png)

Excelファイルは、src/test/resources配下に置きましょう。

ポイントとしては、
**・ヘッダ行が必要です。**

**・nullにしたい場合は、セルを空にします。**

**・シート名は、テーブルの物理名にします。**

**・シートは複数可能です。ここがcsvファイルとの違いで、何テーブルでも処理することができます。
なお、外部キー制約のあるテーブルを更新対象にする場合は、親テーブルを左のシート、子テーブルを右のシートにしましょう。逆順だと、実行時にSQLExceptionになります。**

## テストクラス

・DatabaseConnectionコンストラクタの第2引数には、スキーマ名を指定します。

・execute(iconn, dataSet)メソッドが、実際にテーブルレコードを書き換える処理になります。
下の例では、DatabaseOperation.CLEAN_INSERTとしており、まずテーブルレコードを全削除した上で、Excelに記載されたデータを投入する挙動になります。

```java
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する挙動になります。

## 補足

テストクラスで、データベースの接続情報として、
```java
@Autowired
DataSource dataSource;
```
としていますが、これはapplication.propertiesに下記が定義されていれば、自動的にDIされるので、Configurationで定義する必要はありません。

```properties
spring.datasource.driver-class-name=org.postgresql.Driver
spring.datasource.url=jdbc:postgresql://アドレス:ポート/データベース名
spring.datasource.username=ユーザ名
spring.datasource.password=パスワード
```

下記のような記述は不要↓↓↓

  ```java
  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());
    }
}
```