Mocking and Lists
This lesson gives an overview of the mocking interface, lists, and exceptions in Spock.
We'll cover the following
Mocking
Mocking interfaces is extremely easy in Spock. Simply use the Mock method, as shown in the following example (where Subscriber is an interface):
class APublisher extends Specification {
def publisher = new Publisher()
def subscriber = Mock(Subscriber)
Now subscriber
is a mocked object. We can implement methods simply using the overloaded operator >>
as shown below.
def "can cope with misbehaving subscribers"() {
subscriber.receive(_) >> { throw new Exception() }
when:
publisher.send("event")
publisher.send("event")
then:
2 * subscriber.receive("event")
}
Expected behavior is described by using a number or range times (*
) the method call as shown above.
The under-score (_
) is treated like a wildcard (much like in Scala).
List or tables of data
Much like how JUnit has DataPoints and Theories, Spock allows us to use lists or tables of data in tests.
For example:
def "subscribers receive published events at least once"() {
when: publisher.send(event)
then: (1.._) * subscriber.receive(event)
where: event << ["started", "paused", "stopped"]
}
Above, the overloaded <<
operator is used to provide a list for the event variable. Although it is a
List here, anything that is iterable could be used.
Ranges: The range
1…_
here means “one or more” times. You can also use_…3,
for example, to mean “three or less” times.
Tabular formatted data can be used as well. For example:
def "length of NASA mission names"() {
expect:
name.size() == length
where:
name | length
"Mercury" | 7
"Gemini" | 6
"Apollo" | 6
}
In this case, the two columns (name and length) are used to substitute the corresponding variables in the expect block. Any number of columns can be used.
Added the annotation @Unroll
to a test allows us to run each data input as a separate test using
the given name. We can embed variables in the test name using the #name
syntax.
For example, to unroll the previous Spock example, do the following:
@Unroll
def "length of NASA mission, #name with length, #length"() {
This would cause three tests to be run, one for each mission name.
Expecting exceptions
Use the thrown
method in the then
block to expect a thrown Exception.
def "peek on empty stack throws"() {
when: stack.peek()
then: thrown(EmptyStackException)
}
You can also capture the thrown exception by simply assigning it to thrown()
. For example:
def "peek on empty stack throws"() {
when: stack.peek()
then:
Exception e = thrown()
e.toString().contains("EmptyStackException")
}
Conclusion
As you can see, Spock makes tests more concise and easy to read. But most importantly, it makes the intentions of the test clear.
Get hands-on with 1400+ tech skills courses.