[ ].blue

60 Second Cake

May 15, 2014

An quick example of all the pieces of the "Cake" dependency pattern, from the perspective of a web application...

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
// Dependencies are described on an abstract trait (interface)
trait Dependencies {
  val service: Service
}

// Controllers require an implementation of dependencies by using a self type declaration
// (this: TYPE =>) This tells the Scala compiler to require an implementation in order for
// the class to be initialized.
abstract class Controller extends Behavior { this: Dependencies =>
  ...
}

// Because trait Dependencies is abstract, we can build multiple "environments" or implementations
// The obvious one is the real services
trait ActualImplementation extends Dependencies {
  val service: Service = new Service("foo", "bar")
}

// But it's also possible to make mock versions or anything else for that matter
trait MockImplementation extends Dependencies with Mockito {
  val service: Service = mock[Service]
}

// The actual controller object Play (or whatever framework) uses mixes in actual implementation
// This is how we get the "real" thing when the app is running
object Controller extends Controller with ActualImplementation

// It's common to abstract a controller behavior to it's own trait.  When we do this it's not
// necessary to use the self type declaration.  We can just list the services we need at the
// top and as long as they are the same name it will "just work"
trait Behavior {
  val service: Service
  def doSomething = service.something()
}

// When testing the behavior trait is mixed in with the mock implementation
// This is a win-win as everything is already mocked and the only thing left to do
// is to put any specific stubs you need for the parts you're testing.
class BehaviorTest extends Spec {
  class TestContext extends MockImplementation with Behavior

  "foo" in new TestContext() {
    ...
  }
}