我想为函数编写一个单元测试
import com.github.nscala_time.time.Imports.{DateTime, richReadableInstant}
def myFunction(ts: Long):Long = {
(new DateTime(ts) to DateTime.now()).toDurationMillis
}
是否可以以某种方式模拟 Datetime.now(),以便测试结果不依赖于测试何时运行?
据我所知,这个库是围绕joda-time
的包装器(正如官方文档所建议的那样,应该放弃它以支持java.time
,但我假设你有一些约束迫使你在 Java 8 之前的版本上工作)。
joda-time
附带了一系列静态帮助程序,除其他外,这些帮助程序允许您管理方法请求"当前时间"时的响应(在此处查看其JavaDoc)。
最简单的方法(但由于它所依赖的共享可变状态,可能容易出错)如下所示:
import com.github.nscala_time.time.Imports.{DateTime, richReadableInstant}
import org.joda.time.DateTimeUtils
DateTimeUtils.setCurrentMillisFixed(42)
def myFunction(ts: Long):Long = {
(new DateTime(ts) to DateTime.now()).toDurationMillis
}
assert(myFunction(42) == 0)
你可以在Scastie上玩这个代码。
如前所述,这种方法有点笨拙,并且依赖于共享的可变状态,这使得它容易出现令人困惑的错误。您可以构建一个不错的小助手,以确保您可以在特定测试中使用自定义时钟,并在完成后重置为系统时钟。所需的同步意味着性能下降,但对于测试来说可能是可以接受的。
import com.github.nscala_time.time.Imports.{DateTime, richReadableInstant}
import org.joda.time.DateTimeUtils
import org.joda.time.DateTimeUtils.MillisProvider
def myFunction(ts: Long):Long = {
(new DateTime(ts) to DateTime.now()).toDurationMillis
}
final class FixedClock(at: Long) extends MillisProvider {
override def getMillis(): Long = at
}
def withCustomClock[A](clock: MillisProvider)(f: => A): A = {
synchronized {
try {
DateTimeUtils.setCurrentMillisProvider(clock)
f
} finally {
DateTimeUtils.setCurrentMillisSystem() // _always_ reset to the system clock once done
}
}
}
assert(myFunction(42) > 1000000)
withCustomClock(new FixedClock(at = 42)) {
assert(myFunction(42) == 0)
Thread.sleep(1000)
assert(myFunction(42) == 0)
}
assert(myFunction(42) > 1000000)
您可以在 Scastie 上的另一个工作表上使用另一个示例。