KarateのテストでDBUnitを使ってDBにテストデータを入れる

## はじめに
Javaのテストフレームワークの中に[Karate](https://github.com/karatelabs/karate)というものがあります。
E2Eのテスト自動化ツールで、レポートなども生成してくれて、非常に便利なのですが、テストデータの作成が中々にめんどくさい。
Javaのコードをテスト内で実行できるので、それでORMみたいなものを作ってデータの作成もできるんですが、クラスを作ったらいちいちそっちも作ったり、修正したらメンテしたりとめんどくさいです。

そこで便利なのが、DBUnitです。
XML形式でデータを定義し、あとは流すだけでデータを作ってくれます。
また、XMLの内部で識別用の文字列をセットしておいて、後で任意の値に置換することで変数の入れ込みも行えます。

## コード

以下が該当のテストコードです。

```bash
Feature: テスト
  Background:
    * def dbunit = Java.type('integration.util.DBUnitController')
    * def uuid = Java.type('java.util.UUID')
    * text originalData =
    """
      <?xml version="1.0" encoding="UTF-8"?>
      <dataset>
        <TEST ID="<id>" NAME="テスト" />
      </dataset>
    """
  Scenario: テスト1

    * copy data = originalData
    * def id = uuid.randomUUID().toString()
    * replace data
      | token        | value                                   |
      | id           | id                                      |
    * print data
    * dbunit.refresh(data)
    
    Given url 'http://localhost:9080/api/test?id=' + id
    And header Accept = 'application/json'
    When method get
    Then status 200
    And match response.test.id = id
```

また、dbunitのコードは`DBUnitController.java` に書かれています。
以下が該当のコードです。

```java
public class DBUnitController {
  private static final String schema = "xxx";
  private static final String username = "xxx";
  private static final String password = "xxx";
  private static final String url = "xxx";
  private static final String driverClassName = "xxx";
  private static IDatabaseConnection conn;

  static {
    try {
      conn =
          new JdbcDatabaseTester(driverClassName, url, username, password, schema).getConnection();
    } catch (Exception e) {
      e.printStackTrace();
    }
  }

  public static void refresh(String content) throws Exception {
    try {
      String filename = saveFile(content);
      DatabaseOperation.REFRESH.execute(conn, getDataSet(filename));
    } catch (Exception e) {
      e.printStackTrace();
      throw e;
    }
  }

  private static IDataSet getDataSet(String filename) throws Exception {
    FlatXmlDataSet dataSet = new FlatXmlDataSetBuilder().build(new File(filename));

    ReplacementDataSet replacementDataSet = new ReplacementDataSet(dataSet);
    replacementDataSet.addReplacementObject("[null]", null);
    replacementDataSet.addReplacementObject("[uuid]", UUID.randomUUID().toString());
    replacementDataSet.addReplacementObject(
        "[current_timestamp]",
        LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.000")));
    return replacementDataSet;
  }

  private static String saveFile(String content) throws Exception {
    Path path = Files.createTempFile("dbunit-", ".xml");
    File file = path.toFile();
    Files.write(path, content.getBytes(StandardCharsets.UTF_8));
    file.deleteOnExit();
    return file.getAbsolutePath();
  }
}
```

コード自体はかなり単純で、テーブルのスキーマにデータを定義して、それをDBUnitに渡して保存してもらっているだけです。

karateでは、xmlに `<id>` のような形でデータの部分に定義しておけば、replaceという宣言で、`token` に合致するものを `value` の値に置換してくれます。
上で示したテストコードのように、変数を入れ込むことができるので非常に便利です。

また、私のプロジェクトでは、`DBUnitController.java` の `getDataSet` メソッドの中で、文字列を予約しています。
このように予約しておけば、わざわざxmlで書かなくても、`[uuid]` などと書くだけで、uuidに変換してくれます。
こういった拡張は好きなだけできると思います。

是非試してみてください。