DBUnitを使用した結合試験データの積み込み

# DBUnitを使用した結合試験データの積み込み

先日JBehaveを使った試験の自動化について書きましたが([JBehave+Maven+Eclipseを使った結合試験の自動化](http://qiita.com/tamurashingo@github/items/09e231a13a1c26d85f91))、データベースの準備はテキストでやっていくのはきついので、DBUnitでデータを積み込みます。


## 準備

### データをExcelで定義する

以下のルールを守るだけです。
1ファイルに複数のテーブルの情報を定義できるため、シナリオ試験のデータをまとめるのにちょうど良いです。

* 1テーブル1シート
* 1ファイルには複数のテーブルの情報を定義できる
* シートの名前はテーブル名にする
* 1行目はカラム名を記入する
* xls形式で保存

例）Excelでのデータ定義
![exceldata.png](https://qiita-image-store.s3.amazonaws.com/0/13469/8768d5e1-d6fc-7405-d589-151df8e455c3.png "Excelでのデータ定義")


### pom.xmlの修正

DBUnitを使うための依存情報を追加します。
apache poiはかならず3.2を使います。

```xml:pom.xml
...
  <dependencies>
    ...
    <dependency>
      <groupId>org.dbunit</groupId>
      <artifactId>dbunit</artifactId>
      <version>2.4.9</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.apache.poi</groupId>
      <artifactId>poi</artifactId>
      <version>3.2-FINAL</version>
      <scope>test</scope>
    </dependency>
    ...
  </dependencies>
...
```


## 試験

### データの積み込み

JBehaveを使っているのであればStepファイルに、JUnit等の単体試験であればそのコードに、データ積み込みのコードを追加します。
すでにデータベースにある情報はいったんファイルに吐き出し、試験完了後に戻しています。
積み込み済みのデータ量が多いとバックアップに時間がかかるので、試験専用のスキーマやインスタンスを用意するのが良いと思います。

```java:DBTestStep.java
public class DBTestStep {

    /** データベースのバックアップ */
    private File backupFile;

    @BeforeStories
    public void beforeStories() throws Exception {
    }

    @Given("シナリオデータ積み込み $xlsFile")
    public void loadXls(String xlsFile) throws Exception {

        // ここはそれぞれのConnectionの取得方法に合わせる
        try (IDatabaseConnection conn = new DatabaseConnection(getConnection())) {

            /*-
             * データベースの内容を事前にバックアップ
             */
            QueryDataSet partialDataSet = QueryDataSet(conn);

            // バックアップを取りたいテーブルを列挙
            partialDataSet.addTable("emp");
            partialDataSet.addTable("dept");
            // バックアップ内容を保持するファイルを生成
            backupFile = File.createTempFile("testdb_bak", ".xml");
            // テーブルの内容をファイルに書き込む
            FlatXmlDataSet.write(partialDataSet, new FileOutputStream(backupFile));


            /*-
             * データの積み込み
             */
            String filename = DBTestStep.class.getClassLoader().getResource(xlsFile).getPath();
            IDataSet dataSet = new XlsDataSet(new File(filename));
            new InsertIdentityOperation(DatabaseOperation.CLEAN_INSERT).execute(conn, dataSet);


            // コミットさせたい場合はコミット
            conn.getConnection().commit();
        }
        finally {
          if (conn != null) {
              conn.close();
          }
        }
    }

    @When("申し込み番号 $no の申し込み処理を実施")
    public void doApply(String no) throws Exception {
        ApplyService service = new ApplyServiceImpl();
        service.doApply(no);
    }

    @Then("申し込み番号 $no が申し込み済みであること")
    public void checkApply(String no) throws Exception {
        ApplyService service = new ApplyServiceImpl();
        ApplyService.Status status = service.getApplyStatus(no);

        assertEquals(ApplyService.Status.APPROVED, status);
    }
    

    @AfterStories
    public void afterStories() throws Exception {
        // バックアップしていない場合は戻さない
        if (backupFile == null) {
            return;
        }

        try (IDatabaseConnection conn = new DatabaseConnection(getConnection())) {
            // バックアップファイルから戻す
            IDataSet dataSet = new FlatXmlDataSetBuilder().build(backupFile);
            new InsertIdentityOperation(DatabaseOperation.CLEAN_INSERT).execute(conn, dataSet);
            backupFile = null;

            // 必要に応じてコミット
            conn.getConnection().commit();
        }
        finally {
            if (conn != null) {
                conn.close();
            }
        }
    }

    ....
}
```

### 実施

eclipseからjunitを動かしたり、mvn testをしたり、mvn integration-testで試験を行います。
シナリオ試験が自動で実行されるのも見ていて気持ちが良いですし、すべての試験に合格するのも見ていて気持ちが良いです。


## Trouble shoot

### Date型のデータが登録できない

（時刻を持つ）Date型／Datetime型のカラムを持つテーブルにデータを登録しようとすると、org.dbunit.dataset.datatype.TypeCastExceptionが出てデータが登録できないことがあります。
これは日付の書式が間違っている可能性が非常に高いです。

Excelに記載するときに

* 書式を文字列にする
* YYYY-MM-DD HH:MM:SS形式で日時を定義する

として実行すると、うまく登録できます。

[http://javawock.blog.shinobi.jp/java-%20dbunit/dbunit%E3%81%A7excel%E3%81%8B%E3%82%89%E3%83%87%E3%83%BC%E3%82%BF%E3%82%A4%E3%83%B3%E3%83%9D%E3%83%BC%E3%83%88%E6%99%82%E3%81%AE%E6%97%A5%E4%BB%98%E3%82%AB%E3%83%A9%E3%83%A0%E3%81%8Ctypecastexception](http://javawock.blog.shinobi.jp/java-%20dbunit/dbunit%E3%81%A7excel%E3%81%8B%E3%82%89%E3%83%87%E3%83%BC%E3%82%BF%E3%82%A4%E3%83%B3%E3%83%9D%E3%83%BC%E3%83%88%E6%99%82%E3%81%AE%E6%97%A5%E4%BB%98%E3%82%AB%E3%83%A9%E3%83%A0%E3%81%8Ctypecastexception)