Fake system clock pattern in Scala with implicit parameters
http://www.nurkiewicz.com/2013/07/fake-system-clock-pattern-in-scala-with.html
Fake system clock is a design pattern addressing testability issues of programs heavily relying on system time. If business logic flow depends on current system time, testing various flows becomes cumbersome or even impossible. Examples of such problematic scenarios include:
Fake system clock addresses these issues by abstracting system time over simple interface. Essentially you never call
As you can see I am depending on Joda Time library. Since we are already in the Scala land, one might consider scala-time or nscala-time wrappers. Moreover the abstract name
The standard implementation that you would normally use simply delegates to system time:
For the purposes of unit testing we will develop other implementations, but first let’s focus on usage scenarios. In a typical Spring/JavaEE applications fake system clock can be turned into a dependency that the container can easily inject. This makes dependence on system time explicit and manageable, especially in tests:
Here I am using Spring constructor injection asking the container to provide some
This works great, but becomes painful for certain types of objects, namely entity/DTO beans and utility (
similarly:
It’s not bad from design perspective. Both
Let us refactor our solution a little bit so that
Notice how we call
The compiler tried to find implicit value for
Where
also equivalent (first form is turned into the second by the compiler):
Interestingly in the bytecode level, implicit parameters aren’t any different from normal parameters so if you want to call such method from Java, passing
Of course you are free to put any logic here: advancing time by arbitrary value, speeding it up, etc. You get the idea. Now remember, the reason for
or make it implicit but more specific to the compiler resolution mechanism:
The latter approach is easier to maintain as you don’t have to remember about passing
Also Java and frameworks working on top of our code are not aware of Scala implicit resolution happening at compile time. Therefore e.g. our Spring MVC controller will not work as Spring is not aware of
Fake system clock pattern in general works only when used consistently. If you have even one place when real time is used directly as opposed to
- certain business flow runs only (or is ignored) during weekends
- some logic is triggered only after an hour since some other event
- when two events occur at the exact same time (typically 1 ms precision), something should happen
- …
Fake system clock addresses these issues by abstracting system time over simple interface. Essentially you never call
new Date()
, new GregorianCalendar()
orSystem.currentTimeMillis()
but always rely on this:
1
2
3
4
5
6
7
8
9
| import org.joda.time.{DateTime, Instant} trait Clock { def now() : Instant def dateNow() : DateTime } |
Clock
is not a coincidence. It’s short and descriptive, but more importantly it mimics java.time.Clock
class from Java 8 - that happens to address the same problem discussed here at the JDK level! But since Java 8 is still not here, let’s stay with our sweet and small abstraction.The standard implementation that you would normally use simply delegates to system time:
1
2
3
4
5
6
7
8
9
| import org.joda.time.{Instant, DateTime} object SystemClock extends Clock { def now() = Instant.now() def dateNow() = DateTime.now() } |
1
2
3
4
5
6
7
| @ Controller class FooController @ Autowired() (fooService : FooService, clock : Clock) { def postFoo(name : String) = fooService store new Foo(name, clock) } |
Clock
implementation. Of course in this case SystemClock
is marked as @Service
. In unit tests I can pass fake implementation and in integration tests I can place another,@Primary
bean in the context, shadowing the SystemClock
.This works great, but becomes painful for certain types of objects, namely entity/DTO beans and utility (
static
) classes. These are typically not managed by Spring so it can’t inject Clock
bean to them. This forces us to pass Clock
manually from the last “managed” layer:
1
2
3
4
5
6
| class Foo(fooName : String, clock : Clock) { val name = fooName val time = clock.dateNow() } |
1
2
3
4
5
| object TimeUtil { def firstFridayOfNextMonth(clock : Clock) = //... } |
Foo
constructor andfirstFridayOfNextMonth()
method do rely on system time so let’s make it explicit. On the other hand Clock
dependency must be dragged, sometimes through many layers, just so that it can be used in one single method somewhere. Again, this is not bad per se. If your high level method has Clock
parameter you know from the beginning that it relies on current time. But still is seems like a lot of boilerplate and overhead for little gain. Luckily Scala can help us here with:
implicit
parameters
Let us refactor our solution a little bit so that Clock
is an implicit parameter:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
| @ Controller class FooController(fooService : FooService) { def postFoo(name : String)( implicit clock : Clock) = fooService store new Foo(name) } @ Service class FooService(fooRepository : FooRepository) { def store(foo : Foo)( implicit clock : Clock) = fooRepository storeInFuture foo } @ Repository class FooRepository { def storeInFuture(foo : Foo)( implicit clock : Clock) = { val friday = TimeUtil.firstFridayOfNextMonth() //... } } object TimeUtil { def firstFridayOfNextMonth()( implicit clock : Clock) = //... } |
fooRepository storeInFuture foo
ignoring second clock
parameter. However this alone is not enough. We still have to provide some Clock
instance as second parameter, otherwise compilation error strikes:
1
2
3
4
5
6
7
8
| could not find implicit value for parameter clock : com.blogspot.nurkiewicz.foo.Clock controller.postFoo( "Abc" ) ^ not enough arguments for method postFoo : ( implicit clock : com.blogspot.nurkiewicz.foo.Clock)Unit. Unspecified value parameter clock. controller.postFoo( "Abc" ) ^ |
Clock
parameter but failed. However we are really close, the simplest solution is to use package object:
1
2
3
4
5
6
7
| package com.blogspot.nurkiewicz.foo package object foo { implicit val clock = SystemClock } |
SystemClock
was defined earlier. Here is what happens: every time I call a function with implicit clock: Clock
parameter insidecom.blogspot.nurkiewicz.foo
package, the compiler will discover foo.clock
implicit variable and pass it transparently. In other words the following code snippets are equivalent but the second one provides explicit Clock
, thus ignoring implicits:
1
2
| TimeUtil.firstFridayOfNextMonth() TimeUtil.firstFridayOfNextMonth()(SystemClock) |
1
2
| fooService.store(foo) fooService.store(foo)(SystemClock) |
Clock
instance is mandatory and explicit.implicit clock
parameter seems to work quite well. It hides ubiquitous dependency while still giving possibility to override it. For example in:Tests
The whole point of abstracting system time was to enable unit testing by gaining full control over time flow. Let us begin with a simple fake system clock implementation that always returns the same, specified time:
1
2
3
4
5
| class FakeClock(fixed : DateTime) extends Clock { def now() = fixed.toInstant def dateNow() = fixed } |
implicit
parameter was to hide Clock
from normal production code while still being able to supply alternative implementation. There are two approaches: either pass FakeClock
explicitly in tests:
1
2
3
4
| val fakeClock = new FakeClock( new DateTime( 2013 , 7 , 15 , 0 , 0 , DateTimeZone.UTC)) controller.postFoo( "Abc" )(fakeClock) |
1
2
3
4
| implicit val fakeClock = new FakeClock( new DateTime( 2013 , 7 , 15 , 0 , 0 , DateTimeZone.UTC)) controller.postFoo( "Abc" ) |
fakeClock
to method under test all the time. Of course fakeClock
can be defined more globally as a field or even inside test package object. No matter which technique of providing fakeClock
we choose, it will be used throughout all calls to service, repository and utilities. The moment we given explicit value to this parameter, implicit parameter resolution is ignored.Problems and summary
Solution above to testing systems heavily dependant on time is not free from issues on its own. First of all the implicitClock
parameter must be propagated throughout all the layers up to the client code. Notice that Clock
is only needed in repository/utility layer while we had to drag it up to the controller layer. It’s not a big deal since the compiler will fill it in for us, but sooner or later most of our methods will include this extra parameter.Also Java and frameworks working on top of our code are not aware of Scala implicit resolution happening at compile time. Therefore e.g. our Spring MVC controller will not work as Spring is not aware of
SystemClock
implicit variable. It can be worked around though with WebArgumentResolver
.Fake system clock pattern in general works only when used consistently. If you have even one place when real time is used directly as opposed to
Clock
abstraction, good luck in finding test failure reason. This applies equally to libraries and SQL queries. Thus if you are designing a library relying on current time, consider providing pluggableClock
abstraction so that client code can supply custom implementation likeFakeClock
. In SQL, on the other hand, do not rely on functions like NOW()
but always explicitly provide dates from your code (and thus from custom Clock
).
Comments
Post a Comment