背景
- 現在時刻を使った処理のJUnitテストを書きたい
- ファイルに時刻が埋まっているなど、チェックしたい時刻の変更が面倒
- 現在時刻から1分以内の処理時間かどうかを判定したい
- 判定対象となる時刻はファイルに記載されている
この状態では、テストケースでファイルを用意してもその瞬間しか使えない。またファイル自体は自動生成するのはコストが高すぎる。
例えば、、、以下はStorm UI REST APIでアクセスできる情報。このcomponentErrors内にあるtimeを現在時刻と比較するようなJUnitの場合、色々と面倒。
{
"executors": 1,
"componentErrors": [
{
"time": 1458203307000,
"errorHost": "hostname",
"errorPort": 6701,
"errorWorkerLogLink": "http:\/\/hostname:8000\/log?file=worker-6701.log",
"errorLapsedSecs": 612537,
"error": "java.lang.RuntimeException:
:
:
:
- 現在時刻をいじる? -> システム時刻の変更は何かと危険、できれば避けたい。
- テストデータ読み込み時にtime要素だけ書き換えてファイル上書きする?あるいはテンプレート的に用意しておいて、テスト実施前に埋め込むコードを書く? -> その仕組みを用意するだけでコストかかりそう。
- そもそもファイル読み込みを諦めてJacksonとかに変換したものを使う? -> 本来やりたいことから逃げてるだけ
とまあ、色々考えました。
解消方法
Java 8以上の実行環境であれば、java.time.Clockを利用する。
実装例
[テスト対象のコード]
Clockを現在時刻を扱うメソッドに引き渡すことで実現可能。この書き方に慣れていない自分は若干気持ち悪い実装に感じましたが、これがClockの使い方らしい。
※以下の例では「currentTimeClock」がClockオブジェクト
// ファイルからターゲットとなる時刻のlong値を既に取得済み。
for (Map<String, Object> componentError : componentErrorList)
{
Long targetTime = (Long) componentError.get("time");
if (targetTime == null)
{
continue;
}
long currentTime = currentTimeClock.millis();
if (targetTime <= currentTime && currentTime - targetTime < BORDER_TIME_DIFF)
{
resultChecking = "Failed";
}
}
通常は
Clock.systemDefaultZone()
でインスタンスを生成する。
テスト時は
Clock clock = Clock.fixed(ZonedDateTime.of(2016, 03, 17, 17, 29, 25, 0, ZoneId.systemDefault()).toInstant(), ZoneId.systemDefault());
などの手法でfixed関数を使うと、現在時刻を取得するタイミングでもfixedで指定された時刻が返るようになる。実装例ではmillis()メソッドを利用しているが、以下のように書いてもOK。というか下記の方が一般的な書き方らしい。
LocalDateTime.now(clock);