Patrick van Dissel
Spock is a testing and specification framework for Java and Groovy applications. 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, jMock, RSpec, Groovy, Scala, Vulcans, and other fascinating life forms.
def 'A new stack is empty'() {
def stack = new Stack()
expect:
stack.empty
}
def 'Can push an element onto the stack'() {
given: 'a new stack and element'
def stack = new Stack()
def element = 'push me'
when: 'an element is pushed'
stack.push(element)
then: 'the stack has that element'
!stack.empty
stack.size() == 1
stack.peek() == element
}
def 'Pop from an empty stack throws EmptyStackException'() {
def stack = new Stack()
when:
stack.pop()
then:
thrown(EmptyStackException)
stack.empty
}
def '''Pop from an empty stack throws EmptyStackException
without a message'''() {
def stack = new Stack()
when:
stack.pop()
then:
def e = thrown(EmptyStackException)
!e.message
stack.empty
}
def 'Events are published to all subscribers'() {
def subscriber1 = Mock(Subscriber)
def subscriber2 = Mock(Subscriber)
def publisher = new Publisher()
publisher.add(subscriber1)
publisher.add(subscriber2)
when:
publisher.fire("event")
then:
1 * subscriber1.receive("event")
1 * subscriber2.receive(_ as String)
}
@Unroll
def 'The maximum of the numbers [#a] and [#b] is [#c]'() {
expect:
Math.max(a, b) == c
where:
a | b || c
5 | 1 || 5
3 | 9 || 9
}
def 'Adding an item to a list increases the size by one'() {
given: 'a list with one item'
def names = ['Spock']
when: 'adding one item to the list'
names << 'Groovy'
then: 'the list size increases by one'
names.size() == old(names.size()) + 1
}
def 'Subscriber thrown exception does not impact publisher'() {
def subscriber1 = Mock(Subscriber)
def subscriber2 = Mock(Subscriber)
def publisher = new Publisher()
publisher.add(subscriber1)
publisher.add(subscriber1)
publisher.add(subscriber2)
when:
publisher.fire('event')
then:
1 * subscriber1.receive('event')
1 * subscriber1.receive(_) >> {
throw new RuntimeException()
}
1 * subscriber2.receive(_ as String)
}
GroovyDSL
Sputnik
, Spock’s JUnit runner
Works for Java and Groovy code
Specification-based
(Specification by Example? BDD?)
Data Driven Testing
with data providers and tables
Interaction Based Testing
with build-in support for
mocking, stubbing and spying
import spock.lang.*
import spock.lang.Specification
class MyFirstSpec extends Specification {
// fields
// fixture methods
// feature methods
// helper methods
}
class InstanceFields extends Specification {
def obj = new ClassUnderSpecification()
def coll = new Collaborator()
}
class SharedFields extends Specification {
@Shared
def res = new VeryExpensiveResource()
}
class StaticFields extends Specification {
static final PI = 3.141592654
}
class FixtureMethods extends Specification {
def setup() {} // run before every feature method
def cleanup() {} // run after every feature method
def setupSpec() {} // run before the first feature method
def cleanupSpec() {} // run after the last feature method
}
class FeatureMethods extends Specification {
def 'pushing an element on the stack'() {
// blocks go here
}
}
Set up the feature’s fixture
Provide a stimulus to the system under specification
Describe the response expected from the system
Clean up the feature’s fixture
setup:
def stack = new Stack()
given:
def stack = new Stack()
def stack = new Stack()
when: // stimulus
then: // response
def 'Can push an element onto the stack'() {
given: 'a new stack and element'
def stack = new Stack()
def element = 'push me'
when: 'an element is pushed'
stack.push(element)
then: 'the stack has that element'
!stack.empty
stack.size() == 1
stack.peek() == element
}
Condition not satisfied:
stack.size() == 2
| | |
| 1 false
[push me]
def '''Pop from an empty stack throws EmptyStackException
without a message'''() {
def stack = new Stack()
when:
stack.pop()
then:
def e = thrown(EmptyStackException)
!e.message
stack.empty
}
expect:
Math.max(1, 2) == 2
vs
when:
def x = Math.max(1, 2)
then:
x == 2
def 'Offered PC matches preferred configuration'() {
when:
def pc = shop.buyPc()
then:
matchesPreferredConfiguration(pc)
}
void matchesPreferredConfiguration(PC pc) {
assert pc.vendor == 'Sunny'
assert pc.clockRate >= 2333
assert pc.ram >= 4096
assert pc.os == 'Linux'
}
def 'Adding an item to a list increases the size by one'() {
given: 'a list with one item'
def names = ['Spock']
when: 'adding one item to the list'
names << 'Groovy'
then: 'the list size increases by one'
names.size() == old(names.size()) + 1
}
spock-reports
extension for BDD-style reports@Unroll
def 'The maximum of the numbers [#a] and [#b] is [#c]'() {
expect:
Math.max(a, b) == c
where:
a | b || c
5 | 1 || 5
3 | 9 || 9
}
def 'Events are published to all subscribers'() {
def subscriber1 = Mock(Subscriber)
def subscriber2 = Mock(Subscriber)
def publisher = new Publisher()
publisher.add(subscriber1)
publisher.add(subscriber2)
when:
publisher.fire("event")
then:
1 * subscriber1.receive("event")
1 * subscriber2.receive(_ as String)
}
@spock.lang.Ignore("Reason")
def "my feature"() { ... }
@spock.lang.IgnoreRest
def "I'll run"() { ... }
@spock.lang.IgnoreIf({ os.windows })
def "I'll run everywhere but on Windows"() { ... }
@spock.lang.Requires({ os.windows })
def "I'll only run on Windows"() { ... }
To fail a feature method, fixture, or class
that exceeds a given execution duration,
use spock.lang.Timeout
@Timeout(10)
class TimedSpec extends Specification {
def "I fail after ten seconds"() { ... }
def "Me too"() { ... }
@Timeout(value = 250, unit = TimeUnit.MILLISECONDS)
def "I fail much faster"() { ... }
}
To execute features in the order that they are declared,
use spock.lang.Stepwise
@Stepwise
class RunInOrderSpec extends Specification {
def "I run first"() { ... }
def "I run second"() { ... }
}
To attach a natural-language name to a spec,
use spock.lang.Title
@Title("This is easy to read")
class ThisIsHarderToReadSpec { ... }
To attach a natural-language description to a spec,
use spock.lang.Narrative
@Narrative(""""
As a user
I want foo
So that bar
""")
class GiveTheUserFooSpec { ... }
To indicate one or more subjects of a spec,
use spock.lang.Subject
@Subject([Foo, Bar])
class MySpec { ... }
Subject
can be applied to fields and local variables
@Subject
Foo myFoo
To indicate that a feature or spec
relates to one or more issues in an external tracking system,
use spock.lang.Issue
@Issue("http://my.issues.org/FOO-1")
class MySpec {
@Issue("http://my.issues.org/FOO-2")
def "Foo should do bar"() { ... }
@Issue(["http://my.issues.org/FOO-3",
"http://my.issues.org/FOO-4"])
def "I have two related issues"() { ... }
}
To activate one or more Groovy categories
within the scope of a feature method or spec,
use spock.util.mop.Use
class ListExtensions {
static avg(List list) { list.sum() / list.size() }
}
class MySpec extends Specification {
@Use(listExtensions)
def "can use avg() method"() {
expect:
[1, 2, 3].avg() == 2
}
}
To confine meta class changes to the scope of a
feature method or spec class,
use spock.util.mop.ConfineMetaClassChanges
@Stepwise
class FooSpec extends Specification {
@ConfineMetaClassChanges
def "I run first"() {
when:
String.metaClass.someMethod = { delegate }
then:
String.metaClass.hasMetaMethod('someMethod')
}
def "I run second"() {
when:
"Foo".someMethod()
then:
thrown(MissingMethodException)
}
}
Automatically clean up a field or property
at the end of its lifetime
by using spock.lang.AutoCleanup
@AutoCleanup('dispose')
@AutoCleanup(quiet = true)
Spock | JUnit |
---|---|
Specification | Test class |
|
|
|
|
|
|
|
|
Spock | JUnit |
---|---|
Feature | Test |
Feature method | Test method |
Data-driven feature | Theory |
Condition | Assertion |
Exception condition |
|
Interaction | Mock expectation (e.g. in Mockito) |
If you know Java and JUnit, you are almost ready to go
Java’s dynamic companion lets you do more in less time
No assertion API. No record/replay mocking API. No superfluous annotations. Everything is questioned, and only the essential is kept
Spock' runtime collects a wealth of information, and presents it to you when needed
It’s designed from a user’s perspective
Test-first? Test-last? Unit-level? Integration-level? TDD? BDD?
It tries to give you the flexibility to do it your way
Express your thoughts in a beautiful and highly expressive specification language
With Spock’s interception-based extension mechanism, you can easily create your own extensions
Leverage JUnit’s reporting capabilities
Combines the best features of proven tools like JUnit, jMock, and RSpec, and innovates on top of them
Spock playground
http://webconsole.spockframework.org
Java Testing with Spock (book)
http://manning.com/kapelonis/
Spock Reports Extension
https://github.com/renatoathaydes/spock-reports
Slides & samples
http://ikoodi.nl/talks
https://github.com/pvdissel/talks