2. 1
What’s Sp●ck?
Spock is a high-level testing framework
written in Groovy
by Peter Niederwieser
in 2008
Spock is also
open-source https://github.com/spockframework/spock
enterprise-ready (used by Netflix, Apache Tapestry and Spring)
Spock supports
Java, Groovy, Scala, Kotlin and even JavaScript
Maven, Gradle and Grape (with experimental support for SBT and Bazel)
3. WHAT’S SP●CK?
2
Spock can handle the entire lifecycle of an application.
It can be used to
unit test classes
mock and stub the objects
test the behavior of a story
4. 3
Features of Sp●ck
A highly expressive Business Readable DSL
Fully compatible with JUnit Test Runner
− Runs on Sputnik Test Runner which in turn uses JUnit Test Runner
Meticulous and relevant diagnostics
Inbuilt Mocking framework
Agnostic Tests
Extends through modules for
− Spring
− Android
− Tapestry
− Guice
− Genesis
− Reports, and more…
Tests written in Java,
Groovy, etc
Tests written in
JavaScript
Sputnik
Test
Runner
Test Results
5. 4
Running Sp●ck with Gradle
Configure build.gradle as follows –
apply plugin: "groovy"
repositories {
jcenter()
}
dependencies {
testCompile "org.spockframework:spock-core:1.1-groovy-2.4-rc-2"
}
A custom version of groovy can also be used. Just include it as a dependency –
compile "org.codehaus.groovy:groovy-all:2.4.12"
6. 5
Running Sp●ck with Maven
Add the following dependency in pom.xml –
<dependency>
<groupId>org.spockframework</groupId>
<artifactId>spock-core</artifactId>
<version>1.1-groovy-2.4-rc-2</version>
<scope>test</scope>
</dependency>
Spock comes with its own version of groovy. A custom version of groovy can also be used –
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-all</artifactId>
<version>2.4.12</version>
</dependency>
7. RUNNING SP●CK WITH MAVEN
6
To run Spock tests using mvn test, add the following plugins –
<plugin>
<groupId>org.codehaus.gmavenplus</groupId>
<artifactId>gmavenplus-plugin</artifactId>
<version>1.5</version>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>testCompile</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.18.1</version>
<configuration>
<useFile>false</useFile>
<includes>
<include>**/*Spec.java</include>
<include>**/*Spec.groovy</include>
</includes>
</configuration>
</plugin>
8. 7
Sp●ck Specifications
import spock.lang.Specification
class FirstSpec extends Specification {
def "length of Carmaker name"() {
expect:
"BMW".length() == 3
}
}
Spock tests are written in groovy.
Semicolons are optional.
Method names follow def keyword and can be string.
Every Spock test class is called a Specification because it extends spock.lang.Specification class. By convention, they end with
Spec.
A test method in a Specification is called a feature method, which in turn is equivalent to a JUnit test.
Feature methods are public.
9. 8
Block Taxonomy
Block Use
given: Used for creating objects, setting up data, launching web pages, logging in before testing access controlled
functionality, etc.
setup: an alias of given:
when: Controls the behavior being tested like initiating an action by a mocked object, interacting with an element on a
webpage, etc.
A when: block must be followed by a then: block.
then: Makes assertions about the results of when: block. Requires a preceding when: block
expect: Makes assertions without requiring when: block. Can be used to check the initial states of objects or preconditions of
when: block.
cleanup: Allows a safe tear-down of resources. Analogous to finally block in Java.
where: Used for parametrized feature methods and listing data tables
and: Used to extend a preceding block. Useful to break a huge expect: or when: block into smaller readable blocks
10. 9
Sp●ck Specifications: A Taste of DSL
Consider the following JIRA story –
Create a utility to sum the digits of an integer to unit
Acceptance Criteria
Given When Then
Number = 1234
Digits are summed
Result = 1
Number = 15678 Result = 9
Number = -35567 Result = 8
Number = 0 Result = 0
11. SP●CK SPECIFICATIONS: A TASTE OF DSL
10
The Spock specification for the aforementioned story can be written as –
import spock.lang.Specification
class UnitSumTest extends Specification {
def "Test summation to a unit" () {
given:
def sum = new UnitSum()
expect:
sum.getUnitSum(input) == output
where:
input | output
1234 | 1
15678 | 9
-35567 | 8
0 | 0
}
}
Note the relative similarity between story and test, both describing the same behavior.
12. 11
Sp●ck Specifications: Grouped Assertions
Consider a Java class –
public class User {
...attributes
public User(String username) {
this.username = username;
this.following = new ArrayList<>();
this.posts = new ArrayList<>();
this.registered = Instant.now();
}
...getters and setters
}
13. SP●CK SPECIFICATIONS: GROUPED ASSERTIONS
12
Instead of asserting for individual properties one by one, the assertions can be grouped as follows, using Spock’s with method –
import spock.lang.Specification
import java.time.Instant
class GroupedAssertionsSpec extends Specification {
def "Multiple Assertions on an Object"() {
given:
def user = new User("Emma")
expect:
with(user) {
username == "Emma"
following.isEmpty()
posts.isEmpty()
registered instanceof Instant
}
}
}
14. 13
Sp●ck Specifications: Expecting Exceptions
import spock.lang.Specification
class ExpectExceptionSpec extends Specification {
def "Expect StringIndexOutOfBoundsException"() {
setup:
def str = "Grace is Eternal"
when:
str.getAt(43)
then:
thrown(StringIndexOutOfBoundsException)
}
}
thrown method will pass only if exception is thrown in when: block
15. 14
Sp●ck Specifications: Interrogating Exceptions
import spock.lang.Specification
class InterrogateExceptionSpec extends Specification {
def "Interrogate ArithmeticException"() {
given:
def num = 10
def zero = 0
when:
num / zero
then:
def e = thrown(ArithmeticException)
e.message == "Division by zero"
}
}
This test passes iff ArithmeticException is thrown and the message matches.
16. 15
Sp●ck Specifications: Unrolling
import spock.lang.Specification
class UnrollingSpec extends Specification {
def "Test for Palindrome" () {
given:
def palindrome = new SimplePalindrome()
expect:
palindrome.isPalindrome(str) == expected
where:
str | expected
"BOB" | true
"vIv" | true
"A" | true
"Holly" | false
}
}
• Without unrolling the entire specification runs without informing precisely which iterations failed or passed
• A series of exceptions are thrown and developer has to figure out which exception corresponds to which test failure
17. SP●CK SPECIFICATIONS: UNROLLING
16
The same specification with unrolling enabled –
import spock.lang.Specification
import spock.lang.Unroll
class UnrollingSpec extends Specification {
@Unroll("is #str a palindrome? #answer")
def "Test for Palindrome" () {
given:
def palindrome = new SimplePalindrome()
expect:
palindrome.isPalindrome(str) == expected
where:
str | expected
"BOB" | true
"vIv" | true
"A" | true
"Holly" | false
answer = expected ? "YES" : "NO"
}
}
18. 17
Specification Lifecycle: Fixture Methods
Name When run Similar to JUnit
setup() Before each test @Before
cleanup() After each test @After
setupSpec() Before first test @BeforeClass
cleanupSpec() After last test @AfterClass
The lifeline of various methods in a Specification –
1. setupSpec()
2. setup()
3. feature method 1()
4. cleanup()
5. setup()
6. feature method 2()
7. cleanup()
8. ...
9. cleanupSpec()
19. 18
Resource Sharing
import spock.lang.Shared
import spock.lang.Specification
class ArrayListSpec extends Specification {
@Shared list = new ArrayList<String>()
def "check if list is clear"() {
list.add("Allspark")
when:
list.clear()
then:
list.size() == 0
}
}
Resources that are needed in several features can be declared as @Shared
22. 21
Specification Lifecycle: Revisited
Consider two Specifications SuperSpec and SubSpec, where SubSpec is a child of abstract SuperSpec. When SubSpec is tested, the
Spock lifecycle follows the following lifeline –
1. super setupSpec()
2. sub setupSpec()
3. super setup()
4. sub setup()
5. sub feature method 1()
6. sub cleanup()
7. super cleanup()
8. super setup()
9. sub setup()
10. sub feature method 2()
11. sub cleanup()
12. super cleanup()
13. ...
14. sub cleanupSpec()
15. super cleanupSpec()
Execution of the setupSpec and setup methods proceeds down the inheritance tree
Execution of the cleanupSpec and cleanup methods proceeds up the inheritance tree
25. 24
Conditional Testing
Annotations for conditional testing of Specifications/feature methods –
@Ignore Forces the test runner to skip a particular feature method or specification
class
@IgnoreRest Isolates a single feature method or specification class that should be
executed while the rest of the suite is skipped
@PendingFeature A variant of @Ignore.
Feature methods annotated with @PendingFeature are executed with
the expectation that they will fail. If a feature method annotated with
@PendingFeature actually passes, that is reported as an error.
26. 25
Sp●ck Diagnostics
import spock.lang.Specification
class DiagnosticsSpec extends Specification {
def "check string length"() {
given:
def str = new StringBuilder()
expect:
str.append("Hello").append("-World").replaceAll("-", ", ").toString() == "Hello World"
}
}
given describes the pre-condition
expect is the assertion
27. SP●CK DIAGNOSTICS
26
When DiagnosticsSpec is run, the following message is displayed –
Condition not satisfied:
str.append("Hello").append("-World").replaceAll("-", ", ").toString() == "Hello World"
| | | | | |
| Hello-World Hello-World Hello, World | false
Hello-World | 1 difference (91% similarity)
| Hello(,) World
| Hello(-) World
Hello, World
Expected :Hello World
Actual :Hello, World
<Click to see difference>
at in.naiyer.spock.basic.DiagnosticsSpec.check string length(DiagnosticsSpec.groovy:12)
The values are evaluated at the end of every expression.
The most relevant line of stacktrace is printed.
A diff window is available to compare the expected and actual values.