Spocklight: Writing Assertions for Arguments Mock Methods
In Spock we can also get a hold on the arguments that are passed to method call of a mock and we can write assertions to check the parameters for certain conditions.
When we create a mock in Spock and invoke a method on the mock the arguments are matched using the equals() implementation of the argument type. If they are not equal Spock will tell us by showing a message that there are too few invocations of the method call. Let’s show this with an example. First we create some classes we want to test:
package com.mrhaki.spock
public class ClassUnderTest {
private final Greeting greeting
ClassUnderTest(final Greeting greeting) {
this.greeting = greeting
}
String greeting(final List<Person> people) {
greeting.sayHello(people)
}
}
package com.mrhaki.spock
interface Greeting {
String sayHello(final List<Person> people)
}
package com.mrhaki.spock
@groovy.transform.Canonical
class Person {
String name
}
Now we can write a Spock specification to test ClassUnderTest. We will now use the default matching of arguments of a mock provided by Spock.
package com.mrhaki.spock
import spock.lang.Specification
class SampleSpecification extends Specification {
final ClassUnderTest classUnderTest = new ClassUnderTest()
def "check sayHello is invoked with people in greeting method"() {
given:
final Greeting greeting = Mock()
classUnderTest.greeting = greeting
and:
final List<Person> people = ['mrhakis', 'hubert'].collect { new Person(name: it) }
when:
final String greetingResult = classUnderTest.greeting(people)
then:
1 * greeting.sayHello([new Person(name: 'mrhaki'), new Person(name: 'hubert')])
}
}
When we execute the specification we get a failure with the message that there are too few invocations:
...
Too few invocations for:
1 * greeting.sayHello([new Person(name: 'mrhaki'), new Person(name: 'hubert')]) (0 invocations)
Unmatched invocations (ordered by similarity):
1 * greeting.sayHello([com.jdriven.spock.Person(mrhakis), com.jdriven.spock.Person(hubert)])
...
To capture the arguments we have to use a different syntax for the method invocation on the mock. This time we define the method can be invoked with any number of arguments *_ and then use a closure to capture the arguments. The arguments are passed to the closure as a list. We can then get the argument we want and write an assert statement.
package com.mrhaki.spock
import spock.lang.Specification
class SampleSpecification extends Specification {
final ClassUnderTest classUnderTest = new ClassUnderTest()
def "check sayHello is invoked with people in greeting method"() {
given:
final Greeting greeting = Mock()
classUnderTest.greeting = greeting
and:
final List<Person> people = ['mrhakis', 'hubert'].collect { new Person(name: it) }
when:
final String greetingResult = classUnderTest.greeting(people)
then:
1 * greeting.sayHello(*_) >> { arguments ->
final List<Person> argumentPeople = arguments[0]
assert argumentPeople == [new Person(name: 'mrhaki'), new Person(name: 'hubert')]
}
}
}
We run the specification again and it will fail again (of course), but this time we get an assertion message:
...
Condition not satisfied:
argumentPeople == [new Person(name: 'mrhaki'), new Person(name: 'hubert')]
| | | |
| | | com.jdriven.spock.Person(hubert)
| | com.jdriven.spock.Person(mrhaki)
| false
[com.jdriven.spock.Person(mrhakis), com.jdriven.spock.Person(hubert)]
at com.jdriven.spock.SampleSpecification.check sayHello is invoked with people in greeting method_closure2(SampleSpecification.groovy:25)
...
Code written with Spock 0.7-groovy-2.0