Spock is a testing framework for Java and Groovy applications. The Spock web site1 has this to say about Spock:
What makes it stand out from the crowd is its beautiful and highly expressive specification language. Thanks to its JUnit runner, Spock is compatible with most IDEs, build tools, and continuous integration servers. Spock is inspired from JUnit, RSpec, jMock, Mockito, Groovy, Scala, Vulcans, and other fascinating life forms.
Spock Basics
The basic structure of a test class in Spock is a class that extends Specification and has multiple methods with Strings for names.
Spock processes the test code and allows you to use a simple Groovy syntax to specify tests.
Each test is composed of labeled blocks of code with labels like when, then, and where. The best way to learn Spock is with examples.
A Simple Test
Let’s start by recreating a simple test :
1 def "toString yields the String representation"() {
2 def array = ['a', 'b', 'c'] as String[]
3 when:
4 def arrayWrapper = new ArrayWrapper<String>(array);
5 then:
6 arrayWrapper.toString() == '[a, b, c]'
7 }
As shown, assertions are simply groovy conditional expressions. If the == expression returns false, the test will fail and Spock will give a detailed printout to explain why it failed.
In the absence of any when clause, you can use the expect clause instead of then; for example:
1 def "empty list size is zero"() {
2 expect: [].size() == 0
3 }
Mocking
Mocking interfaces is extremely easy in Spock2. Simply use the Mock method, as shown in the following example (where Subscriber is an interface):
1 class APublisher extends Specification {
2 def publisher = new Publisher()
3 def subscriber = Mock(Subscriber)
Now subscriber is a mocked object. You can implement methods simply using the overloaded >> operator as shown next. The following example throws an Exception whenever receive is called:
1 def "can cope with misbehaving subscribers"() {
2 subscriber.receive(_) >> { throw new Exception() }
3
4 when:
5 publisher.send("event")
6 publisher.send("event")
7
8 then:
9 2 * subscriber.receive("event")
10 }
Expected behavior is described by using a number or range multiplied by (*) the method call, as shown here.
The underscore (_) is treated like a wildcard (much like in Scala).
Lists or Tables of Data
Much like how JUnit has DataPoints and Theories, Spock allows you to use lists or tables of data in tests.
For example:
1 def "subscribers receive published events at least once"() {
2 when: publisher.send(event)
3 then: (1.._) * subscriber.receive(event)
4 where: event << ["started", "paused", "stopped"]
5 }
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 fewer” times.
Tabular formatted data can be used as well. For example:
1 def "length of NASA mission names"() {
2 expect:
3 name.size() == length
4
5 where:
6 name | length
7 "Mercury" | 7
8 "Gemini" | 6
9 "Apollo" | 6
10 }
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.
Expecting Exceptions
Use the thrown method in the then block to expect a thrown Exception.
1 def "peek on empty stack throws"() {
2 when: stack.peek()
3 then: thrown(EmptyStackException)
4 }
You can also capture the thrown Exception by simply assigning it to thrown(). For example:
1 def "peek on empty stack throws"() {
2 when: stack.peek()
3 then:
4 Exception e = thrown()
5 e.toString().contains("EmptyStackException")
6 }
Summary
As you can see, Spock makes tests more concise and easy to read, and, most importantly, makes the intentions of the test clear.
Footnotes
2 You can also Mock classes, but it requires including the cglib JAR as a dependency.