|
UsingMocks
How to specify expected calls to other objects
Mocks presentationspecs offers a lightweight framework to create mocks and stubs (see this reference and that one for a discussion on mocks and stubs). Using mocks follows a 4 steps process:
object mySpec extends Specification with scala.specs.mock.Mocker {...} class MockedService extends Service {
override def executeService = record
} object mySpec extends Specification with Mocker {
"my system" should {
"use mocks" in {
val mock = new MockedService
expect {
mock.executeService
}
...
}
}
}A complete exampleHere is a complete example of the use of mocks to specify the interactions between a Button and a Light: trait ButtonAndLightMock extends ButtonAndLight with Mocker {
val mock = new Light {
override def on = record
override def off = record
}
val button = Button(mock)
}
trait ButtonAndLight {
case class Button(light: Light) {
var lightOn = false
def push = {
if (lightOn) light.off else light.on
lightOn = !lightOn
}
}
case class Light() {
var state: LightState = Off
def on = state = On
def off = state = Off
def isOn = state == On
}
abstract sealed class LightState(s: String)
object On extends LightState("on")
object Off extends LightState("off")
}
object mockExample extends Specification with ButtonAndLightMock {
"A button and light mock example" should {
"not fail if the mock receives the expected messages" in {
expect {mock.on; mock.off}
button.push
button.push // if the button is pressed twice, then the light will go on and off
}
}
}Mock protocolsBy default, the mock expectations order is not important and any excess message will be ignored. This default decision is motivated by the need to make the specification more robust in case of a minor implementation changes (such as the order of calls). However you can specify that the calls must happen in sequence: expect(inSequence) { mock.on; mock.off }You can also specify that unexpected calls must be reported as failures: expect(exclusively) { mock.on; mock.off }Repeated callsYou may want to be even more precise when specifying mock expectations by using the following ProtocolTypes:
expect(anyOf) {mock.on; mock.off}
expect(oneOf) {mock.on; mock.off}
3.times {i => button.push} // will fail
expect(5.Of) {mock.on}
Nested expectationsYou can also nest expectations to build more sophisticated expectations. In that case, you can even avoid the expect function: expect(exclusively) {
1.of {mock.on; mock.off; mock.on}
1.of {mock.off}
}Return values and expected parameter valuesHow to stub return valuesYou may want your mock to return specific values sometimes (using it effectively as a stub). In that case, you can use the recordAndReturn method to return a specific value: trait MockedRandomGenerator extends RandomGenerator with Mocker {
// will always return 0.5
override def getRandomNumber: Double = recordAndReturn(0.5)
}If the return value depends on the mocked method parameters, you can use a function to compute the returned value, using the method parameters (see here for an example) How to define expected parametersYou may want to check that the parameters passed to the mocked method verify some specific constraints. In order to do that, you need to build your mock object accordingly. Here's n example: // define a mock with a function which will check the passed parameters
def buildMock(f: (Int, Movie) => Boolean) = {
new MovieRater {
override def okForAge(a: Int, m: Movie) = recordAndReturn(f(a, m))
}
}
// create a new mock object checking that the age is > 0
val mock = buildMock((a: Int, m: Movie) => {a must beGreaterThan(0); true})How to avoid to create complex parameters in expectationsSome methods can only be called with complex objects as parameters. Thus, it can be tedious when defining expected calls because such objects need to be build first. In order to avoid this, you can use the any function: expect {
mock.methodWithComplexParameter(any[ComplexType])
}
|
Sign in to add a comment
I copied and pasted your "A complete example" I'm getting type mismatch errors
and an "missing argument" on
I'm using specs-1.2.5 thx
I have update the example which is now working with Scala > 2.7.5