Java 8 的时间 API 允许通过在 Instant.now(clock)
中传入自定义时钟
来进行模拟。调用。同样,LocalDateTime.now(clock)
.
我在自定义缓存实现中有一个 Deadline
对象,用于指示条目是否过期。我希望能够通过在单元测试中不使用 sleep
方法来测试这一点。 Scala 的 scala.concurrent.duration 包中的类是否提供任何插件模拟功能?
最佳答案
似乎没有一种简单的方法可以使用默认的 Deadline
类来执行此操作,因为它在内部依赖于您无法覆盖的系统时钟。
一种选择是您推出自己的 Deadline 类,其中注入(inject)了一个 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 类非常简单,因此创建自己的 Deadline 类应该不会太困难。然而,一个缺点是 Clock 似乎支持的最小时间单位是毫秒。
除此之外,我很好奇您为什么选择在实现中使用Deadline
。根据我对您的用例的有限理解,您可以尝试其他潜在的替代方案:
- 为缓存中的每个条目分配一个过期时间,并在下次访问时检查它是否应该失效/删除
- 如果缓存支持异步性,您可以使用某种 IO/任务数据类型根据条目的过期时间安排删除条目。然而,随着缓存的增长,这种方法可能无法很好地扩展
- 使用支持 TTL 的第三方缓存库。一个例子是 Play 框架中的缓存 API,它作为一个独立的库提供,您无需携带框架的其余部分即可使用。我相信 Google Guava 库也提供了一些不同的缓存实现
然而,这里的主要问题并不是测试 Duration 很困难,而是与 Deadline 的实现方式有关。当测试使用依赖注入(inject)的代码与在内部定义所有内容的代码(例如,其初始化(即构造函数或其他什么))时,也会出现同样的问题。
就人们如何测试 Duration
而言,它通常与异步代码一起用于超时目的(即 Future、Task、IO),因此人们不会经常在其上测试 Duration自己的。对于测试异步代码,ScalaTest 是您可以使用的几个测试库之一。
关于scala - 单元测试期间 Scala Deadline 的模拟时间,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50955230/