模拟Scala中的函数调用以进行单元测试

iszxjhcz  于 2022-11-09  发布在  Scala
关注(0)|答案(1)|浏览(189)

我想写一个函数的单元测试

import com.github.nscala_time.time.Imports.{DateTime, richReadableInstant}

def myFunction(ts: Long):Long = {
  (new DateTime(ts) to DateTime.now()).toDurationMillis
}

是否可以以某种方式模拟Datetime.now(),以便测试结果不依赖于测试运行的时间?

3wabscal

3wabscal1#

在我看来,这个库是一个围绕joda-time的 Package 器(正如官方文档所建议的那样,应该放弃它,转而使用java.time,但我假设您有一些限制,迫使您使用Java 8之前的发行版)。
joda-time附带了一组静态助手,其中允许您管理方法请求“当前时间”时的响应(请参阅他们的JavaDoc here)。
最简单的可能方式(但由于它依赖的共享可变状态可能容易出错)如下所示:

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)

您可以使用这个代码here on 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)

您可以在this other worksheet on Scastie上试用另一个示例。

相关问题