Mocking and Lists

This lesson gives an overview of the mocking interface, lists, and exceptions in Spock.

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.