$shibayu36->blog;

株式会社はてなでエンジニアをしています。プログラミングや読書のことなどについて書いています。

【Scala】テスト時にjoda-timeの現在時刻を変更できるjoda-time-fakeを作りました

テストをしようとすると、現在時刻を変更したくなることがよくある。現在のプロジェクトではjoda-timeを使っているので、joda-timeのDateTime.nowが返す時刻を変更したい。ということで、それを簡単にできるjoda-time-fakeというライブラリを作りました。

インストール

sbtの場合はlibraryDependenciesに入れる。

libraryDependencies ++= Seq(
  "com.github.shibayu36" %% "joda-time-fake" % "0.0.2"
)

使い方

README.mdに書いてありますが、以下のように使います。

import com.github.shibayu36.jodatimefake.FakeTimer
import org.joda.time.DateTime

// 現在のepoch (ms) で現在時刻を変更する
val result = FakeTimer.fake(1515974400000L) {
    println(DateTime.now.toString) // 2018-01-15T00:00:00.000Z
    "hoge"
}

// 現在時刻はブロックを抜けると戻る
println(DateTime.now.toString)

// ブロックで返した値を受け取ることができる
println(result) // hoge

// joda-timeのDateTimeインスタンスを渡して現在時刻を変更可能
FakeTimer.fake(new DateTime(2018, 2, 13, 14, 59)) {

}

// ISODateTimeFormatの形式を指定して現在時刻を変更可能
FakeTimer.fake("2018-03-02T12:34:56+09:00") {

}

// ブロックの中でさらに時刻を進めたい場合は、fakeWithTimerを使う
// 渡ってくるTimerのインスタンスのtick()メソッドを呼ぶと時間を進められる
FakeTimer.fakeWithTimer(1515974400000L) { t =>
    println(DateTime.now.toString) // 2018-01-15T00:00:00.000Z
    t.tick(3000) // 3000ms時間を進める
    println(DateTime.now.toString) // 2018-01-15T00:00:03.000Z
}

注意

このライブラリはjoda-timeのDateTimeUtilsのsetCurrentMillisProviderというのを使っているのだけど、これがマルチスレッドに対応していません。そのため、マルチスレッド環境下でテストを動かしている場合、このライブラリもうまく動きません。

マルチプロセスなら動くので、並列でテストしたい場合はマルチプロセスを使うと良さそうです。

まとめ

joda-timeから得られる現在時刻を変更するライブラリであるjoda-time-fakeを作りました。どうぞご利用ください。

Scalaのエコシステムを知るためにという意図もあって、簡単なライブラリを作って公開してみたけど、実際に学ぶことが非常に多かったように思う。いろいろな知見に関してはこれから少しずつ公開していく予定です。