Groovy Testing
Framework/DSL
Star Trek?
Specification & Mock
Groovy = very close to Java optionally typed language
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<!-- … -->
<dependencies>
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-all</artifactId>
<version>2.5.8</version>
<scope>test</scope>
<type>pom</type>
</dependency>
<dependency>
<groupId>org.spockframework</groupId>
<artifactId>spock-core</artifactId>
<version>1.3-groovy-2.5</version>
<scope>test</scope>
</dependency>
<!-- Byte Buddy enables mocking of classes (in addition to interfaces)
see http://spockframework.org/spock/docs/1.3/all_in_one.html#_mocking_classes -->
<dependency>
<groupId>net.bytebuddy</groupId>
<artifactId>byte-buddy</artifactId>
<version>1.10.3</version>
<scope>test</scope>
</dependency>
<!-- Objenesis enables mocking of classes without default constructor (together with CGLib)
see http://spockframework.org/spock/docs/1.3/all_in_one.html#_mocking_classes -->
<dependency>
<groupId>org.objenesis</groupId>
<artifactId>objenesis</artifactId>
<version>3.1</version>
<scope>test</scope>
</dependency>
<!-- … -->
</dependencies>
Explicit Groovy version
Spock dependency
Ability to mock non-interfaces
<build>
<sourceDirectory>src/main/java</sourceDirectory>
<testSourceDirectory>src/test/groovy</testSourceDirectory>
<plugins>
<!-- Configure GMavenPlus to compile Groovy test sources
see https://github.com/groovy/GMavenPlus/wiki/Usage -->
<plugin>
<groupId>org.codehaus.gmavenplus</groupId>
<artifactId>gmavenplus-plugin</artifactId>
<version>1.8.1</version>
<executions>
<execution>
<goals>
<goal>addTestSources</goal>
<goal>compileTests</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
<!-- Spock tests end with "Spec" -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M4</version>
<configuration>
<includes>
<include>**/*Test.*</include>
<include>**/*Spec.*</include>
</includes>
</configuration>
</plugin>
</plugins>
</build>
</project>
Use GMavenPlus to compile Groovy test code
Make Surefire find *Spec files
(not only *Test)
plugins {
id 'java'
id 'groovy'
}
repositories {
mavenLocal()
mavenCentral()
}
dependencies {
testImplementation 'org.codehaus.groovy:groovy-all:2.5.8'
testImplementation 'org.spockframework:spock-core:1.3-groovy-2.5'
testImplementation 'net.bytebuddy:byte-buddy:1.10.3'
testImplementation 'org.objenesis:objenesis:3.1'
testImplementation 'ch.qos.logback:logback-classic:1.2.3'
compileOnly 'org.slf4j:slf4j-api:1.7.29'
}
sourceCompatibility = '11'
Groovy plugin
Spock &
Groovy dependencies
Ability to mock
non-interfaces
class MySpec extends Specification {
User user = createUser(LocalDate.now().minusYears(23))
@Shared JsonSlurper jsonSlurper = new JsonSlurper()
static final PI = 3.141592654
def setupSpec() { /* before first feature method */ }
def setup() { /* before each feature method */ }
def cleanup() { /* after each feature method */ }
def cleanupSpec() { /* after last feature method */ }
def "isOfAge returns true"() {
expect:
!user.isOfAge()
}
boolean createUser(LocalDate age) {
return new User(LocalDate.now().minusYears(17))
}
}
extend spock.lang.Specification
field initialization before each feature
fixture methods
feature method
shared field initialization only once per spec
static field for constants only
helper method
SuperSpec
SubSpec
setup()
setupSpec()
cleanup()
setup()
"feature"()
setupSpec()
cleanup()
cleanupSpec()
cleanupSpec()
class SuperSpec extends Specification {
def setupSpec() { println "setup super spec" }
def setup() { println "setup super" }
def cleanup() { println "cleanup super" }
def cleanupSpec() { println "cleanup super spec" }
}
class SubSpec extends SuperSpec {
def setupSpec() { println "setup sub spec" }
def setup() { println "setup sub" }
def "feature"() {
expect:
true
}
def cleanup() { println "cleanup sub" }
def cleanupSpec() { println "cleanup sub spec" }
}
1
2
3
4
5
6
8
7
9
given
when
then
expect
Setup
Stimulous
Response
Cleanup
cleanup
Phase
Block
optional
define variables
no semantic
requires then
single line best
combine when/then
conditions only!
every line
optional
feature specific cleanup
class MySpec extends Specification {
def "given, when, then"() {
given:
User user = new User("jdoe", "John", "Doe", LocalDate.now().plusYears(18))
when:
boolean ofAge = user.isOfAge()
then:
ofAge
}
def "when, then"() {
when:
boolean ofAge = new User("jdoe", "John", "Doe", LocalDate.now().plusYears(18)).isOfAge()
then:
ofAge
}
def "given, expect"() {
given:
User user = new User("jdoe", "John", "Doe", LocalDate.now().plusYears(18))
expect:
user.isOfAge()
}
def "expect"() {
expect:
new User("jdoe", "John", "Doe", LocalDate.now().plusYears(18)).isOfAge()
}
}
Let's code ;)
→ https://github.com/mkutz/spock-testing-workshop/#part-1-blocks-expectations-and-pending-features
class MathSpec extends Specification {
def "maximum of two numbers"() {
expect:
Math.max(1, 3) == 3
Math.max(7, 4) == 7
Math.max(0, 0) == 0
}
}
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
}
}