Moje zdjęcie
Software Craftsman's Blog by Marcin Pieciukiewicz
Java and Scala development

Tuesday, July 30, 2013

Faking System Clock with metaprogramming pattern in Scala

Tomasz Nurkiewicz has written an article Fake system clock pattern in Scala with implicit parameters on his blog. The problem he addresses is that sometimes you require to test your business code against a given date, ie. your system should behave differently on the weekends.

Obviously this will be impossible (or very hard) to handle if a programmers would directly use new Date(), System.curentTimeMills() or some similar construct provided by JVM. So Tomasz proposed an application wide Clock trait that is kind of proxy to Joda Time:
import org.joda.time.{DateTime, Instant}
 
trait Clock {
  def now(): Instant
  def dateNow(): DateTime
}
With the default implementation:
import org.joda.time.{Instant, DateTime}
 
object SystemClock extends Clock {
  def now() = Instant.now()
  def dateNow() = DateTime.now()
}
I like this approach very much, because it provides a clean way to grab the current date, and it decouples us from specific Time library (Joda Time in this case). It is quite obvious how programmer should grab the current system time:
val currentTime = SystemClock.now
Now let's back to initial problem, how to pass a specific time if we want to test our code? I propose to use the method I've described in Metaprogramming in Scala with higher order functions article.


First Step - Create companion object for clock
Let's create a companion object Clock that will provide, to a programmer, a kind of proxy to default clock implementation, so the programmer won't have to use SystemClock directly:
object Clock {
  private val clockInstance: Clock = SystemClock
  def now() = clockInstance.now()
  def dateNow() = clockInstance.dateNow()
}
And now the usage of our Clock has been simplified to this:
val currentTime = Clock.now

Second step - Allow to change clock instance
To solve our problem of testing against specific time we would like to allow programmer to change the clock instance that is used in Clock companion object:
object Clock {
  private var clockInstance: Clock = SystemClock
  def now() = clockInstance.now()
  def dateNow() = clockInstance.dateNow()
 
  def changeClock(newClock:Clock) {
    clockInstance = newClock
  }
}
Now the programmer can switch the instance of Clock in the following way:
//alternative Clock implementation
class FakeClock(fixed: DateTime) extends Clock {
  def now() = fixed.toInstant
  def dateNow() = fixed
}

//change of clock that is used
Clock.changeClock(new FakeClock(new DateTime(2013, 7, 15, 0, 0, DateTimeZone.UTC)))
//code to be tested
...
val currentTime = Clock.now
...
//revert the clock change
Clock.changeClock(SystemClock)
As you can see we allowed a programmer to force the Clock object to return specific date instead of current system date. To use it in tests a programmer only have to call Clock.changeClock function before tested code, and a tested code will "see" that current time is different.  


Final step - change Clock instance by declaration
In the final step we'll change our Clock.changeClock function to higher order function so it's usage will be much nicer and it will allow to change clock instance temporarily only.
object Clock {
  private var clockInstance: Clock = SystemClock
  def now() = clockInstance.now()
  def dateNow() = clockInstance.dateNow()
 
  def changeClock(newClock:Clock)(codeBlock: => Unit) {
  val oldClock = Clock.clockInstance
    clockInstance = newClock
    codeBlock
    clockInstance = oldClock
  }
}
So now to change a clock instance a programmer could write something like this:
import Clock.changeClock

// changing clock instance only for the  given block of code
changeClock(new FakeClock(new DateTime(2012, 2, 2, 0, 0, DateTimeZone.UTC))) {
  ...
  val currentTime = Clock.now
  ...
}

Usage example
This is a little more complicated usage example, but it's a nice way to demonstrate how easy the change of clock is now:
object ClockTest {

  import Clock.changeClock

  def testedMethod() {
    println(Clock.dateNow())
  }

  def main(args: Array[String]) {
    testedMethod()

    changeClock(new FakeClock(new DateTime(2013, 7, 15, 0, 0, DateTimeZone.UTC))) {

      testedMethod()

        changeClock(new FakeClock(new DateTime(2012, 2, 2, 0, 0, DateTimeZone.UTC))) {
          testedMethod()
        }

      testedMethod()
    }

    testedMethod()
  }

}
And the output of this simple program would be this (let's assume that now is 30th of July 2013):
2013-07-30T19:02:38.501+02:00
2013-07-15T00:00:00.000Z
2012-02-02T00:00:00.000Z
2013-07-15T00:00:00.000Z
2013-07-30T19:02:38.594+02:00
As you can see this worked very well even for nested blocks of changeClock.


The complete implementation is available on gist.github.com.


Previous article: Metaprogramming in Scala with higher order functions

No comments:

Post a Comment