概要
Scalaで現在時刻を取得するような処理を含むメソッドをテストする方法をいくつか考えてみました。
雑なTipsですm(_ _)m
現在時刻を取得する関数をデフォルト引数にする
こちらは現在時刻をデフォルト引数にする方法です。
// テスト対象のクラス
class DefaultArgTime {
def getCurrentDayOfMonth(now: LocalDateTime = LocalDateTime.now()): Int =
now.getDayOfMonth
}
// テストコード
class DefaultArgTimeSpec extends org.specs2.mutable.Specification {
"#getCurrecntDayOfMonth" should {
"return" in {
new DefaultArgTime().getCurrentDayOfMonth(
LocalDateTime.of(2020, 1, 1, 1, 1)
) === 1
}
}
}
テストコードでは単に現在時刻を引数として指定してやればいいだけなので、お手軽かと思います。個人的には一番よく使う方法です。
ただ、DefaultArgTime
の呼び出し元からは、自由にLocalDateTime
型を渡せてしまうので、getCurrecntDayOfMonth
は意図しない呼び出し方がされる危険性もあるかと思います。
テスト時に時刻を取得するメソッドをoverrideする
こちらは、現在時刻を取得する処理をprotectedメソッドで定義してやって、テスト時にoverrideするという方法です。
// テスト対象のクラス
class TraitTime {
def getCurrentDayOfMonth(): Int = now.getDayOfMonth
protected def now: LocalDateTime = LocalDateTime.now()
}
// テストコード
class TraitTimeSpec extends org.specs2.mutable.Specification {
"#getCurrentDayOfMonth" should {
"return" in {
val traitTime = new TraitTime() {
override protected def now: LocalDateTime =
LocalDateTime.of(2020, 1, 1, 1, 1)
}
traitTime.getCurrentDayOfMonth() === 1
}
}
}
こちらの方法のメリットとしては、(先ほどの現在時刻をデフォルト引数として渡す方法と比較すると)publicメソッドの引数にnow
のような余計な引数を定義する必要がないということが考えられます。
ただ、現在時刻を取得するprotectedメソッドを用意して、テスト時にoverrideするのは若干面倒ですね。
specs2-mockでspyする
こちらはspecs2-mockのspy
メソッドを使った方法です。
// テスト対象のクラス
class SpyTime {
def getCurrentDayOfMonth(): Int =
now.getDayOfMonth
def now: LocalDateTime = LocalDateTime.now()
}
// テストコード
class SpyTimeSpec extends Specification with Mockito {
"#getCurrentDayOfMonth" should {
"return" in {
val spyTime = spy(new SpyTime())
spyTime.now returns LocalDateTime.of(2020, 1, 1, 1, 1)
spyTime.getCurrentDayOfMonth() === 1
}
}
}
now
を余計にpublicメソッドとして定義する必要があるので、無駄がありますが、場合によってはこの方法もアリかと思います。