A Spock Tutorial for Javaer

What is it?

The description of its github repository:

The Enterprise-ready testing and specification framework.


A Quote from the office website:

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, RSpec, jMock, Mockito, Groovy, Scala, Vulcans, and other fascinating life forms.


Spock Framework Reference Documentation

Advantage

  • Specifications as Documentation (BDD); given-when-then ; generating-report.

  • less code, more readable, some awesome feature:

    • blocks, (no)thrown, ‘==’,
    • where, table, database,
    • with, verifyAll
    • more fluent Mock syntax
  • support JUnit, it’s suppot all @Rules in JUnit.

Comparision

  • JUnit, testNG
  • Cucumber(Gherkin). “one file solution”
  • Java BDD framework, like: JBehave
  • RSpec spock/issues/106

Spock Quick Start

pom.xml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
</dependencies>
<!-- spock -->
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-all</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.spockframework</groupId>
<artifactId>spock-core</artifactId>
<version>1.2-groovy-2.4</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.spockframework</groupId>
<artifactId>spock-spring</artifactId>
<version>1.2-groovy-2.4</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.athaydes</groupId>
<artifactId>spock-reports</artifactId>
<version>1.6.1</version>
<scope>test</scope>
</dependency>
</dependencies>


<build>
<plugins>
<plugin>
<groupId>org.codehaus.gmavenplus</groupId>
<artifactId>gmavenplus-plugin</artifactId>
<version>1.6</version>
<executions>
<execution>
<goals>
<goal>compileTests</goal>
</goals>
</execution>
</executions>
</plugin>

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<includes>
<include>**/*Test.java</include>
<include>**/*Spec.java</include>
</includes>
</configuration>
</plugin>
</plugins>
</build>

Usage Example

Comparision to JUnit

Blocks

Blocks

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def "events are published to all subscribers"() {
given:
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("event")
}

Conditions

Within the then and expect blocks, assertions are implicit

some Shorthand (syntactic sugar in Groovy)

The test code wrote by Spock are more concise and easier to read.
There’s less clutter, boilerplate code, and your test cases can be structured better.

e.g:

Java’s == is actually Groovy’s is() method, and Groovy’s == is a clever equals()!

1
2
str == "content"     // assert the equality between strings
list == [1, 2, 3, 4] // assert the equality between lists
1
2
publisher.subscribers << subscriber // << is a Groovy shorthand for List.add()
publisher.subscribers - subscriber // to remove element from List

see also:
groovy-lang.org/syntax.html
some demos

Exception Conditions

thrown, notThrown

1
2
3
4
5
6
when:
stack.pop()

then:
thrown(EmptyStackException)
stack.empty

Support Java Testing Lib

including JUnit, Mockito, JAssert, Hamcrest, etc.

Data Tables

A awesome feature!

1
2
3
4
5
6
7
8
9
10
11
12
class MathSpec extends Specification {
def "maximum of two numbers"(int a, int b, int c) {
expect:
Math.max(a, b) == c

where:
a | b | c
1 | 3 | 3
7 | 4 | 7
0 | 0 | 0
}
}

Data Pipes

1
2
3
4
5
...
where:
a << [1, 7, 0]
b << [3, 4, 0]
c << [3, 7, 0]

Migration from Java to Groovy

java lambda expression vs groovy closure

java:

1
2
() 
e.g: Stream.of(1, 2, 3).map(i -> i * 2)

groovy:

1
2
{}
e.g: Stream.of(1, 2, 3).map { i -> i * 2 }

[] vs {}

java:

1
@ContextConfiguration(classes = {A.class, B.class})

groovy:

1
2
3
@ContextConfiguration(classes = [A.class, B.class])
@ContextConfiguration(classes = [A, B]) // the '.class' suffix can also be ignored
String[] arr = ["a", "b", "c"]

groovy handy no-setter (or no-getter) syntax

declare a field without access modifier

Precaution

  • IntelliJ-IDEA: must set test directory otherwise it reports Empty Test Suite error.


  • JUnit @ClassRule
    1
    2
    3
    4
    5
    6
    7
    8
    9
    // it works:
    @ClassRule
    @Shared
    public MyRule rule = new MyRule()

    // doesn't work (with static modifier):
    @ClassRule
    @Shared
    public static MyRule rule = new MyRule()

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!

TDD 在企业开发中的实践 上一篇
SpringBoot 原理分析 下一篇