OITA: Oika's Information Technological Activities

@oika 情報技術的活動日誌。

DbUnit : テストデータの日付・時刻を相対的な値で指定する

やりたいこと:DbUnit のテストデータで、DATETIMEなどのカラムに、「1時間前」「1日前の12:00」みたいな、現在日時からの相対時刻を入れたい。

たとえば直近3日間の履歴を取得して返すAPIのテストであれば、3日前あたりの境界値レコードがテストデータとして入っていてほしい。

前提

  • DbUnit : v2.7.0
  • DB は MySQL で検証
  • テストデータは XML

方法1:RelativeDateTimeParser を使う

RelativeDateTimeParser https://javadoc.io/doc/org.dbunit/dbunit/latest/index.html の記法を使って定義する。バージョン 2.7 以降。
基本的にはこれで十分。

<history id="1" record_time="[now]" />       <!--現在日時-->
<history id="2" record_time="[now-2h]" />    <!--2時間前-->
<history id="3" record_time="[now-1d 23:59:00]" />  <!--昨日の23時59分-->

方法2:ReplacementDataSet で置換する

もうちょっと自分で好きなようにやりたいときは、DataSetLoaderの挙動を上書きする。
AbstractDataSetLoader を継承したクラスで、createDataSet 内で以下のように。

明示的にNULLを入れたい場合なども同じ方法でできる。

public class MyDataSetLoader extends AbstractDataSetLoader {
    @Override
    protected IDataSet createDataSet(Resource resource) throws Exception {
        FlatXmlDataSetBuilder builder = new FlatXmlDataSetBuilder();

        try (InputStream inputStream = resource.getInputStream()) {
            return createReplacementDataSet(builder.build(inputStream));
        }
    }

    private ReplacementDataSet createReplacementDataSet(FlatXmlDataSet dataSet) {
        ReplacementDataSet replacementDataSet = new ReplacementDataSet(dataSet);

        DateTimeFormatter dtFmt = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        LocalDateTime now = LocalDateTime.now();

        replacementDataSet.addReplacementObject("[NOW]", now.format(dtFmt));
        replacementDataSet.addReplacementObject("[2_HOUR_BEFORE]", now.minusHours(2).format(dtFmt));

        return replacementDataSet;
    }
}

もうちょっと真面目にやるなら正規表現など使って頑張る感じでしょう。
これをテストクラスに対して @DbUnitConfiguration(dataSetLoader = MyDataSetLoader.class) で指定する。

テストデータはこんな感じで。

<history id="1" record_time="[NOW]" />
<history id="2" record_time="[2_HOUR_BEFORE]" />

ただこの方法だと、 record_time="[1_DAY_BEFORE] 23:59:00" みたいに値の一部分だけを文字列で置換するようなことはできないっぽかった(軽く試した感じ)。
そういうことがしたければ、 [1_DAY_BEFORE 23:59:00] のように値全体を置換対象にして、自前で parser 書いてやる必要があるでしょう。やりたくないですね。

別解

そもそもあちこちからシステムの現在時刻に依存しているようなコードはテスタブルでないので、一般的には、どこかのレイヤで外部から現在時刻を注入する口を作るような設計を考えたほうが良い。

APIであればリクエストで基準日を指定できるようにするとか。

以上。