Home > other >  Mock a function call in scala for unittesting
Mock a function call in scala for unittesting

Time:10-22

I would like to write a unittest to the function

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

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

Is it possible to somehow mock Datetime.now(), so that the test result does not depend on when the test is running?

CodePudding user response:

From what I can see this library is a wrapper around joda-time (which, as the official documentation recommends, should be dropped in favor of java.time, but I assume you have some constraint that forces you to work on a pre-Java 8 release).

joda-time comes with a collection of static helpers that, among other things, allow you to manage what the response is when a method asks for the "current time" (see their JavaDoc here).

The easiest possible way (but possibly prone to mistakes due to the shared mutable state it relies on) would look like the following:

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)

You can play around with this code here on Scastie.

As mentioned, this approach is a bit clunky and relies on shared mutable state, which makes it prone to confusing errors. You can build a nice small helper to make sure you can use a custom clock on a specific test and reset to the system clock once done. The required synchronization means a performance hit, but it's probably acceptable for your tests.

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)

You can play around with this other example here on this other worksheet on Scastie.

  • Related