ssgao
ssgao

Reputation: 5521

Mocking time for Scala Deadline during unit testing

Java 8's time API allows mocking by passing in a custom clock in Instant.now(clock) invocations. Similarly, LocalDateTime.now(clock).

I have a Deadline object in a custom Cache implementation that indicates whether an entry is expired. I would like to be able to test this by not using sleep method in unit test. Does classes in Scala's scala.concurrent.duration package provide any plugin mocking capabilities?

Upvotes: 2

Views: 1012

Answers (1)

Luis Medina
Luis Medina

Reputation: 1152

There doesn’t seem to be an easy way to do this using the default Deadline class because internally it relies on the system clock which you can’t override.

One option would be for you to roll your own Deadline class that has a Clock injected to it kind of like this:

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’s Deadline class is simple enough so creating your own shouldn’t be too difficult. One downside, however, is that the smallest unit of time that Clock seems to support is milliseconds.

This aside, I’m curious as to why you chose to use Deadline in your implementation. Other potential alternatives you can try based on my limited understanding of your use-case:

  • Assign every entry in the cache an expiration time and check it the next time that it’s accessed to see whether it should be invalidated/removed
  • If the cache supports asynchronicity, you could schedule removal of entries based on their expiration time using some sort of IO/Task data type. This approach might not scale well, however, as your cache grows
  • Use a 3rd party caching library that supports TTL. one example is the Cache API from the Play Framework which is offered as a standalone library you can use without bringing along the rest of the framework. I believe the Google Guava library also offers a few different cache implementations as well

The main issue here, however, isn’t so much that testing Duration is hard, but has more to do with how Deadline was implemented. It’s the same problem that arises when testing code that uses dependency injection vs code that defines everything internally in, say, its initialization (ie. a constructor or what have you).

In terms of how people test Duration, it's usually something that is commonly used alongside asynchronous code for timeout purposes (ie. Future, Task, IO) so people don’t often test Duration on its own. For testing asynchronous code, ScalaTest is one of several testing libs that you can use.

Upvotes: 2

Related Questions