单元测试期间Scala截止日期的模拟时间

时间:2018-06-20 18:56:42

标签: scala time

Java 8的时间API通过在Instant.now(clock)调用中传入自定义clock来进行模拟。同样,LocalDateTime.now(clock)

我在自定义缓存实现中有一个Deadline对象,该对象指示条目是否过期。我希望能够通过在单元测试中不使用sleep方法来对此进行测试。 Scala的scala.concurrent.duration包中的类是否提供任何插件模拟功能?

1 个答案:

答案 0 :(得分:1)

使用默认的Deadline类似乎并不是一种简单的方法,因为它在内部依赖于您无法覆盖的系统时钟。

一个选择是让您滚动自己的Deadline类,并为其注入Clock,如下所示:

case class Deadline(clock: Clock, time: FiniteDuration) {
  def +(other: FiniteDuration): Deadline = copy(time = time + other)
  def -(other: FiniteDuration): Deadline = copy(clock = clock, time = time - other)
  def -(other: Deadline): FiniteDuration = time - other.time
  def timeLeft: FiniteDuration = this - Deadline.now
  def isOverdue: Boolean = (time.toMillis - clock.millis()) < 0
  def hasTimeLeft: Boolean = !isOverdue
}

object Deadline {
  def now: Deadline = {
    val clock = Clock.systemDefaultZone()
    Deadline(clock, Duration(clock.millis(), TimeUnit.MILLISECONDS))
  }
}

Scala的Deadline类非常简单,因此创建自己的类应该不太困难。但是,缺点之一是Clock似乎支持的最小时间单位是毫秒。

此外,我很好奇您为什么选择在实现中使用Deadline。根据我对用例的有限了解,您可以尝试其他可能的选择:

  • 为缓存中的每个条目分配一个过期时间,并在下次访问时检查它,以查看是否应将其无效/删除
  • 如果缓存支持异步性,则可以使用某种IO / Task数据类型,根据条目的到期时间来计划删除条目。但是,随着缓存的增长,这种方法可能无法很好地扩展
  • 使用支持TTL的第三方缓存库。一个示例是Play框架中的Cache API,它作为一个独立的库提供,您无需使用框架的其余部分即可使用它。我相信Google Guava库也提供了一些不同的缓存实现

然而,这里的主要问题并不是很难测试持续时间,而是更多有关“截止日期”的实施方式。测试使用依赖注入的代码与在内部定义所有内容的代码(例如,构造函数或您拥有的东西)进行测试时,会出现相同的问题。

就人们如何测试Duration而言,它通常与异步代码一起用于超时(例如,Future,Task,IO),因此人们不必经常单独测试Duration。对于测试异步代码,ScalaTest是可以使用的多个测试库之一。