SlideShare a Scribd company logo
Spock
Dominik Przybysz
https://github.com/alien11689/spock-show
alien11689@gmail.com @alien11689 http://przybyszd.blogspot.com
Context
Person.groovy
@Canonical
class Person {
String firstName
String lastName
Integer age
boolean isAdult() {
age >= 18
}
}
PersonValidator.java Part. 1
@Component
public class PersonValidator {
public void validatePerson(Person person) {
String firstName = person.getFirstName();
if (firstName == null || firstName.length() == 0) {
throw new PersonValidationException("First
name must be given");
}
String lastName = person.getLastName();
if (lastName == null || lastName.length() == 0) {
throw new PersonValidationException("Last name
must be given");
}
PersonValidator.java Part. 2
Integer age = person.getAge();
if (age == null){
throw new PersonValidationException("Age must be
given");
}
if( age < 0) {
throw new PersonValidationException("Age cannot
be negative");
}
}
}
PersonValidationException.java
public class PersonValidationException extends
RuntimeException {
public PersonValidationException(String message) {
super(message);
}
}
PersonDao.groovy Part. 1
@Component
class PersonDao {
final JdbcTemplate jdbcTemplate
@Autowired
PersonDao(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate
}
PersonDao.groovy Part. 2
void persist(List<Person> persons) {
persons.each {
persist(it)
}
}
@Transactional
void persist(Person person) {
jdbcTemplate.execute("Insert into person
(first_name, last_name, age) values ('${person.
firstName}', '${person.lastName}', ${person.age})")
}
PersonDao.groovy Part. 3
List<Person> findByLastName(String lastName) {
jdbcTemplate.queryForList("select first_name,
last_name, age from person where last_name = ?",
[lastName] as Object[])
.collect({Map row -> new Person(row.first_name,
row.last_name, row.age)
})
}
void close() {
println "Closing person dao"
}
}
PersonController.groovy Part. 1
@Component
class PersonController {
final PersonValidator personValidator
final PersonDao personDao
@Autowired
PersonController(PersonValidator personValidator,
PersonDao personDao) {
this.personValidator = personValidator
this.personDao = personDao
}
PersonController.groovy Part. 2
void addPerson(Person person) {
personValidator.validatePerson(person)
personDao.persist(person)
}
}
PersonContextConfiguration.groovy Part. 1
@Configuration
@ComponentScan("com.blogspot.przybyszd.spock")
class PersonContextConfiguration {
@Bean
JdbcTemplate getJdbcTemplate(DataSource dataSource){
return new JdbcTemplate(dataSource)
}
PersonContextConfiguration.groovy Part. 2
@Bean
DataSource getDataSource() {
BasicDataSource basicDataSource = new
BasicDataSource()
basicDataSource
.setDriverClassName("org.h2.Driver")
basicDataSource
.setUrl("jdbc:h2:mem:personDB;DB_CLOSE_DELAY=1000;
INIT=runscript from 'classpath:db/person.sql';")
basicDataSource.setUsername("sa")
basicDataSource.setPassword("")
return basicDataSource
}
}
Introduction
Dependencies
compile 'org.codehaus.groovy:groovy-all:2.4.0'
compile 'org.springframework:spring-jdbc:4.0.5.RELEASE'
compile 'org.springframework:spring-beans:4.0.5.RELEASE'
compile 'org.springframework:spring-context:4.0.5.RELEASE'
compile 'commons-dbcp:commons-dbcp:1.4'
compile 'com.h2database:h2:1.4.178'
testCompile 'org.spockframework:spock-core:1.0-groovy-2.4'
testCompile 'org.spockframework:spock-spring:1.0-groovy-
2.4'
testCompile 'org.springframework:spring-test:4.0.5.
RELEASE'
testCompile 'cglib:cglib-nodep:3.1'
testCompile 'org.objenesis:objenesis:2.1'
when-then blocks
class PersonTest extends Specification {
def "should set first name from constructor"() {
when:
Person person = new Person(firstName: "Bob")
then:
person.firstName == "Bob"
}
}
Test failed output
person.firstName == "Bob"
| | |
| Bb false
| 1 difference (66% similarity)
| B(-)b
| B(o)b
com.blogspot.przybyszd.spock.dto.Person(Bb, null, null)
Block with description
def "should set first name from constructor 2"() {
when: "person with set first name"
Person person = new Person(firstName: "Bob")
then: "person has first name"
person.firstName == "Bob"
}
Given block
def "should set first name from setter"() {
given:
Person person = new Person(firstName: "Bob")
when:
person.firstName = 'Tom'
then:
person.firstName == "Tom"
}
Multiple asserts
def "should set person data from constructor"() {
when:
Person person = new Person("Bob", "Smith", 15)
then:
person.firstName == "Bob"
person.lastName == "Smith"
person.age == 15
}
Multiple when then
def "should set first name from constructor and change
with setter"() {
when:
Person person = new Person(firstName: "Bob")
then:
person.firstName == "Bob"
when:
person.firstName = "Tom"
then:
person.firstName == "Tom"
}
And block
def "should set first name and last name"() {
when:
Person person = new Person(firstName: "Bob",
lastName: "Smith")
then:
person.firstName == "Bob"
and:
person.lastName == "Smith"
}
Expect block
def "should compare person with equals"() {
expect:
new Person("Bob", "Smith", 15) == new Person
("Bob", "Smith", 15)
}
Lifecycle
Test fields
class LifecycleSpockTest extends Specification {
@Shared
StringWriter writer
Person person
}
Setup specification
def setupSpec() {
println "In setup spec"
writer = new StringWriter()
}
Setup each test
def setup() {
println "In setup"
person = new Person(firstName: "Tom", lastName:
"Smith", age: 21)
}
Cleanup each test
def cleanup() {
println "In cleanup"
person = null
}
Cleanup specification
def cleanupSpec() {
println "In cleanup spec"
writer.close()
}
Setup and clenup blocks
def "should check firstName"() {
setup:
println "setup in test"
println "should check firstName"
expect:
person.firstName == "Tom"
cleanup:
println "Cleanup after test"
}
Statements without block
def "should check lastName"() {
println "should check lastName"
expect:
person.lastName == "Smith"
}
Parameters
Parameters in table
@Unroll
def "should set person data"() {
when:
Person person = new Person(lastName: lastName,
firstName: firstName, age: age)
then:
person.firstName == firstName
person.lastName == lastName
person.age == age
where:
lastName | firstName | age
"Smith" | "John" | 25
"Kowalski" | "Jan" | 24
}
Parameters in method signature
@Unroll
def "should set person data 2"(String firstName, String
lastName, int age) {
// …
where:
lastName | firstName | age
"Smith" | "John" | 25
"Kowalski" | "Jan" | 24
}
Parameters in method name
@Unroll
def "should set person with #lastName, #firstName and
#age"() {
// …
where:
lastName | firstName | age
"Smith" | "John" | 25
"Kowalski" | "Jan" | 24
}
Call parameterless method in test name
Part 1
@Unroll("should set person with #lastName.length(),
#firstName.toUpperCase() and #age")
Call parameterless method in test name
Part 2
@Unroll("should set person with #lastName.length(),
#firstName.toUpperCase() and #age when last name starts
with #firstLetter")
def "should set person with lastName, firstName and age
3"() {
//…
where:
lastName | firstName | age
"Smith" | "John" | 25
"Kowalski" | "Jan" | 24
firstLetter = lastName.charAt(0)
}
Separeted table
@Unroll
def "should check if person is adult with table"() {
expect:
new Person(age: age).isAdult() == adult
where:
age || adult
17 || false
18 || true
19 || true
}
Parameters from list
@Unroll
def "should check if person is adult with list"() {
expect:
new Person(age: age).isAdult() == adult
ageSquare == age * age
where:
age << [17, 18, 19]
adult << [false, true, true]
ageSquare = age * age
}
Parameters from list of list
@Unroll
def "should check if person is adult with list 2"() {
expect:
new Person(age: age).isAdult() == adult
where:
[age, adult] << [[17,false], [18,true], [19,
true]]
}
One paramter table
@Unroll
def "should set first name"() {
when:
Person person = new Person(firstName: firstName)
then:
person.firstName == firstName
where:
firstName | _
"John" | _
"Jan" | _
}
Parameters from db - setup
static Sql sql = Sql.newInstance("jdbc:h2:mem:", "sa",
"", "org.h2.Driver")
def setupSpec() {
sql.execute("""DROP TABLE IF EXISTS person;
CREATE TABLE person (
first_name VARCHAR(256) NOT NULL,
last_name VARCHAR(256) NOT NULL,
age INT NOT NULL
);""")
sql.executeInsert("""INSERT INTO person (first_name,
last_name, age) VALUES
('Tom', 'Smith', 24),
('Jan', 'Kowalski', 30);""")
}
Parameters from db - cleanup
def cleanupSpec() {
sql.close()
}
All parameters from db
@Unroll
def "should set person data with #lastName, #firstName
and #age"() {
// …
where:
[firstName, lastName, age] << sql.rows("SELECT *
FROM person;")
}
All parameters from db by name
@Unroll
def "should set person data with #lastName, #firstName
and #age"() {
// …
where:
[firstName, lastName, age] << sql.rows("SELECT
first_name, last_name, age FROM person;")
}
Drop last parameter
@Unroll
def "should set person data with #lastName, #firstName
and #age"() {
// …
where:
[firstName, lastName] << sql.rows("SELECT * FROM
person;")
}
Omit parameter
@Unroll
def "should set person data with #lastName, #firstName
and #age"() {
// …
where:
[_, lastName, age] << sql.rows("SELECT * FROM
person;")
}
Exceptions
Not thrown exception
PersonValidator sut = new PersonValidator()
def "should pass validation"() {
given:
Person person = new Person(firstName: "Tom",
lastName: "Smith", age: 30)
when:
sut.validatePerson(person)
then:
notThrown(PersonValidationException)
}
Not thrown exception - fails
Expected no exception of type 'com.blogspot.przybyszd.
spock.bean.PersonValidationException' to be thrown, but
got it nevertheless
Thrown exception
@Unroll
def "should not pass validation"() {
then:
PersonValidationException exception = thrown
(PersonValidationException)
exception.message == message
where:
firstName | lastName | age | message
"Tom" | "Smith" | -1 | "Age cannot be
negative"
"" | "Kowalski" | 19 | "First name must
be given"
"Jan" | null | 19 | "Last name must
be given"
}
Thrown exception - another exception
Expected exception of type 'com.blogspot.przybyszd.
spock.bean.PersonValidationException', but got 'java.
lang.RuntimeException'
Thrown exception - but no exception
Expected exception of type 'com.blogspot.przybyszd.
spock.bean.PersonValidationException', but no exception
was thrown
Mocking and stubbing
Creating mock
JdbcTemplate jdbcTemplate = Mock(JdbcTemplate)
PersonDao sut = new PersonDao(jdbcTemplate)
Validate mock calls
def "should persist one person"() {
given:
Person person = new Person("John", "Smith", 20)
when:
sut.persist(person)
then:
1 * jdbcTemplate.execute("Insert into person
(first_name, last_name, age) values ('John', 'Smith',
20)")
}
Mock not called
Too few invocations for:
1 * jdbcTemplate.execute("Insert into person
(first_name, last_name, age) values ('John', 'Smith',
20)") (0 invocations)
Unmatched invocations (ordered by similarity):
None
Too many calls
1 * jdbcTemplate.execute("Insert into person
(first_name, last_name, age) values ('John', 'Smith',
20)") (2 invocations)
Matching invocations (ordered by last occurrence):
2 * jdbcTemplate.execute('Insert into person
(first_name, last_name, age) values ('John',
'Smith', 20)') <-- this triggered the error
Another parameters in calls
def "should persist many persons"() {
given:
List<Person> persons = [new Person("John",
"Smith", 20), new Person("Jan", "Kowalski", 15)]
when:
sut.persist(persons)
then:
1 * jdbcTemplate.execute("Insert into person
(first_name, last_name, age) values ('John', 'Smith',
20)")
1 * jdbcTemplate.execute("Insert into person
(first_name, last_name, age) values ('Jan', 'Kowalski',
15)")
}
Any parameter
then:
2 * jdbcTemplate.execute(_)
Range of calls
then:
(1..3) * jdbcTemplate.execute(_)
At least one call
then:
(1.._) * jdbcTemplate.execute(_)
Any amount of calls
then:
_ * jdbcTemplate.execute(_)
Two calls of method of any mock
then:
2 * _.execute(_)
Two calls of any method of mock
then:
2 * jdbcTemplate._(_)
Two calls of method by regex
then:
2 * jdbcTemplate./exe.*/(_)
Closure validates call
then:
2 * jdbcTemplate.execute({
String sql -> sql.endsWith("('John', 'Smith',
20)") || sql.endsWith("('Jan', 'Kowalski', 15)")
})
Sequential calls
def "should persist many persons in order"() {
given:
List<Person> persons = [new Person("John",
"Smith", 20), new Person("Jan", "Kowalski", 15)]
when:
sut.persist(persons)
then:
1 * jdbcTemplate.execute("Insert into person
(first_name, last_name, age) values ('John', 'Smith',
20)")
then:
1 * jdbcTemplate.execute("Insert into person
(first_name, last_name, age) values ('Jan', 'Kowalski',
15)")
}
Define mock interactions in given block
given:
jdbcTemplate = Mock(JdbcTemplate) {
2 * execute({
String sql -> sql.endsWith("('John',
'Smith', 20)") || sql.endsWith("('Jan', 'Kowalski',
15)")
})
}
Stub
def "should find one person"() {
given:
jdbcTemplate.queryForList("select first_name,
last_name, age from person where last_name = ?",
["Kowalski"]) >> [[first_name: "Jan", last_name:
"Kowalski", age: 20]]
expect:
sut.findByLastName("Kowalski") == [new Person
("Jan", "Kowalski", 20)]
}
Stub in context
given:
jdbcTemplate = Stub(JdbcTemplate) {
queryForList("select first_name, last_name, age
from person where last_name = ?", ["Kowalski"]) >>
[[first_name: "Jan", last_name: "Kowalski", age: 20]]
}
Any stub parameters
def "should find many times person"() {
given:
jdbcTemplate.queryForList(_, _) >> [[first_name:
"Jan", last_name: "Kowalski", age: 20]]
expect:
sut.findByLastName("Kowalski") == [new Person
("Jan", "Kowalski", 20)]
sut.findByLastName("Kowalski") == [new Person
("Jan", "Kowalski", 20)]
}
Multiple return values
def "should find many times person 2"() {
given:
jdbcTemplate.queryForList(_, _) >> [[first_name:
"Jan", last_name: "Kowalski", age: 20]] >>
[[first_name: "Jan", last_name: "Kowalski", age: 25]]
expect:
sut.findByLastName("Kowalski") == [new Person
("Jan", "Kowalski", 20)]
sut.findByLastName("Kowalski") == [new Person
("Jan", "Kowalski", 25)]
}
Multiple return values as list
def "should find many times person 3"() {
given:
jdbcTemplate.queryForList(_, _) >>> [
[[first_name: "Jan", last_name:
"Kowalski", age: 20]],
[[first_name: "Jan", last_name:
"Kowalski", age: 15]]]
expect:
sut.findByLastName("Kowalski") == [new Person
("Jan", "Kowalski", 20)]
sut.findByLastName("Kowalski") == [new Person
("Jan", "Kowalski", 15)]
}
Side effects
def "should throw exception on second find"() {
given:
jdbcTemplate.queryForList(_, _) >>
[[first_name: "Jan", last_name:
"Kowalski", age: 20]] >>
{ throw new
DataRetrievalFailureException("Cannot retrieve data") }
expect:
sut.findByLastName("Kowalski") == [new Person
("Jan", "Kowalski", 20)]
when:
sut.findByLastName("Kowalski")
then:
thrown(DataAccessException)
}
Mocking and stubbing
def "should find one person and check invocation"() {
when:
List result = sut.findByLastName("Kowalski")
then:
result == [new Person("Jan", "Kowalski", 20)]
1 * jdbcTemplate.queryForList(_, _) >>
[[first_name: "Jan", last_name: "Kowalski", age: 20]]
}
Any parameter list
then:
1 * jdbcTemplate.queryForList(*_) >> [[first_name:
"Jan", last_name: "Kowalski", age: 20]]
Validate parameter value
then:
1 * jdbcTemplate.queryForList(_, !(["Smith"] as
Object[])) >> [[first_name: "Jan", last_name:
"Kowalski", age: 20]]
Validate parameter is not null
then:
1 * jdbcTemplate.queryForList(!null, _) >>
[[first_name: "Jan", last_name: "Kowalski", age: 20]]
Interaction block
def "should find one person and check invocation
external with first parameter not null"() {
when:
List result = sut.findByLastName("Kowalski")
then:
result == [new Person("Jan", "Kowalski", 20)]
interaction {
queryForListCalledOnceWithFirstName()
}
}
void queryForListCalledOnceWithFirstName(){
1 * jdbcTemplate.queryForList(!null, _) >>
[[first_name: "Jan", last_name: "Kowalski", age: 20]]
}
Spies
List sut = Spy(ArrayList, constructorArgs: [10])
def "should use spy on list"() {
given:
sut.add(1) >> {
callRealMethod()
}
sut.size() >> 10
when:
sut.add(1)
then:
sut.size() == 10
sut.get(0) == 1
}
Spring
Spring from configuration class
@ContextConfiguration(classes =
PersonContextConfiguration)
class PersonContextFromClassTest extends Specification
{
@Autowired
PersonController personController
@Autowired
PersonDao personDao
//…
}
Spring from configuration xml
@ContextConfiguration(locations = "classpath:
personContext.xml")
class PersonContextFromXmlTest extends Specification {
@Autowired
PersonController personController
@Autowired
PersonDao personDao
//…
}
Helper methods
Without helper
def "should check person"() {
when:
Person result = new Person("Tom", "Smith", 20)
then:
result != null
result.firstName == "Tom"
result.lastName == "Smith"
result.age == 20
}
Boolean helper
def "should check person with boolean helper method"()
{
when:
Person result = new Person("Tom", "Smith", 20)
then:
checkPerson(result, "Tom", "Smith", 20)
}
boolean checkPerson(Person person, String firstName,
String lastName, int age) {
person != null &&
person.firstName == firstName &&
person.lastName == lastName &&
person.age == age
}
Boolean helper - output
checkPerson(result, "Tom", "Smith", 20)
| |
false com.blogspot.przybyszd.spock.dto.Person
(Tom, Smith, 20)
Helper with assert
def "should check person with assert helper method"() {
when:
Person result = new Person("Tom", "Smith", 20)
then:
checkPersonWithAssert(result, "Tom", "Smith",
20)
}
void checkPersonWithAssert(Person person, String
firstName, String lastName, int age) {
assert person != null
assert person.firstName == firstName
assert person.lastName == lastName
assert person.age == age
}
Helper with assert - output
person.firstName == "John"
| | |
| Tom false
| 3 differences (25% similarity)
| (T)o(m-)
| (J)o(hn)
com.blogspot.przybyszd.spock.dto.Person(Tom, Smith, 20)
With
def "should set first name, last name and age 1"() {
when:
Person person = new Person(firstName: "Bob",
lastName: "Smith", age: 40)
then:
with(person) {
firstName == "Bob"
lastName == "Smith"
age == 40
}
}
Annotations
Ignore
class IgnoreTest extends Specification {
def "test 1"() {
expect:
1 == 1
}
@Ignore
def "test 2"() {
expect:
1 == 1
}
def "test 3"() {
expect:
1 == 1
}
}
IgnoreRest
class IgnoreRestTest extends Specification {
def "test 1"() {
expect:
1 == 1
}
@IgnoreRest
def "test 2"() {
expect:
1 == 1
}
def "test 3"() {
expect:
1 == 1
}
}
IgnoreIf
class IgnoreIfTest extends Specification {
// @IgnoreIf({os.windows})
// @IgnoreIf({os.linux})
@IgnoreIf({ System.getProperty("os.name").contains
("Linux") })
def "test 1"() {
expect:
1 == 1
}
}
Requires
class RequiresTest extends Specification {
// @Requires({os.windows})
// @Requires({os.linux})
@Requires({ System.getProperty("os.name").contains
("windows") })
def "test 1"() {
expect:
1 == 1
}
}
AutoCleanup
class AutoCleanupTest extends Specification {
JdbcTemplate jdbcTemplate = Mock(JdbcTemplate)
@AutoCleanup(value = "close", quiet = true)
PersonDao sut = new PersonDao(jdbcTemplate)
def "test 1"() {
expect:
sut != null
}
}
FailsWith
class FailsWithTest extends Specification {
@FailsWith(RuntimeException)
def "test 1"() {
expect:
throw new RuntimeException()
}
}
Timeout
class TimeoutTest extends Specification {
@Timeout(value = 750, unit = TimeUnit.MILLISECONDS)
def "test 1"() {
expect:
1 == 1
}
}
Title
@Title("Title annotation is tested in this
specification")
class TitleTest extends Specification {
def "test 1"() {
expect:
1 == 1
}
}
Narrative
@Narrative("""Multiline narrative annotation
is tested in this specification""")
class NarrativeTest extends Specification {
def "test 1"() {
expect:
1 == 1
}
}
Subject
@Subject(Person)
class SubjectTest extends Specification {
@Subject
Person person = new Person("John", "Smith", 21)
def "should be adult"() {
expect:
person.isAdult()
}
}
Issue
class IssueTest extends Specification {
@Issue(["http://example.org/mantis/view.php?
id=12345", "http://example.org/mantis/view.php?id=23"])
def "test 1"() {
expect:
1 == 1
}
}
Extra
void instead of def
void "should set first name from constructor"() {
when:
Person person = new Person(firstName: "Bob")
then:
person.firstName == "Bob"
}
Shoud instead of def
Should "set first name from constructor"() {
when:
Person person = new Person(firstName: "Bob")
then:
person.firstName == "Bob"
}
Shoud instead of def - how it works
import java.lang.Void as Should
Q & A
Thank you

More Related Content

What's hot

BDD - Behavior Driven Development Webapps mit Groovy Spock und Geb
BDD - Behavior Driven Development Webapps mit Groovy Spock und GebBDD - Behavior Driven Development Webapps mit Groovy Spock und Geb
BDD - Behavior Driven Development Webapps mit Groovy Spock und Geb
Christian Baranowski
 
Spock and Geb in Action
Spock and Geb in ActionSpock and Geb in Action
Spock and Geb in Action
Christian Baranowski
 
The Ring programming language version 1.9 book - Part 53 of 210
The Ring programming language version 1.9 book - Part 53 of 210The Ring programming language version 1.9 book - Part 53 of 210
The Ring programming language version 1.9 book - Part 53 of 210
Mahmoud Samir Fayed
 
The Ring programming language version 1.5.4 book - Part 44 of 185
The Ring programming language version 1.5.4 book - Part 44 of 185The Ring programming language version 1.5.4 book - Part 44 of 185
The Ring programming language version 1.5.4 book - Part 44 of 185
Mahmoud Samir Fayed
 
Map/reduce, geospatial indexing, and other cool features (Kristina Chodorow)
Map/reduce, geospatial indexing, and other cool features (Kristina Chodorow)Map/reduce, geospatial indexing, and other cool features (Kristina Chodorow)
Map/reduce, geospatial indexing, and other cool features (Kristina Chodorow)
MongoSF
 
Groovy puzzlers jug-moscow-part 2
Groovy puzzlers jug-moscow-part 2Groovy puzzlers jug-moscow-part 2
Groovy puzzlers jug-moscow-part 2
Evgeny Borisov
 
The Ring programming language version 1.3 book - Part 34 of 88
The Ring programming language version 1.3 book - Part 34 of 88The Ring programming language version 1.3 book - Part 34 of 88
The Ring programming language version 1.3 book - Part 34 of 88
Mahmoud Samir Fayed
 
The Ring programming language version 1.6 book - Part 46 of 189
The Ring programming language version 1.6 book - Part 46 of 189The Ring programming language version 1.6 book - Part 46 of 189
The Ring programming language version 1.6 book - Part 46 of 189
Mahmoud Samir Fayed
 
The Ring programming language version 1.7 book - Part 48 of 196
The Ring programming language version 1.7 book - Part 48 of 196The Ring programming language version 1.7 book - Part 48 of 196
The Ring programming language version 1.7 book - Part 48 of 196
Mahmoud Samir Fayed
 
The Ring programming language version 1.2 book - Part 32 of 84
The Ring programming language version 1.2 book - Part 32 of 84The Ring programming language version 1.2 book - Part 32 of 84
The Ring programming language version 1.2 book - Part 32 of 84
Mahmoud Samir Fayed
 
The Ring programming language version 1.4.1 book - Part 13 of 31
The Ring programming language version 1.4.1 book - Part 13 of 31The Ring programming language version 1.4.1 book - Part 13 of 31
The Ring programming language version 1.4.1 book - Part 13 of 31
Mahmoud Samir Fayed
 
Swift - 혼자 공부하면 분명히 안할테니까 같이 공부하기
Swift - 혼자 공부하면 분명히 안할테니까 같이 공부하기Swift - 혼자 공부하면 분명히 안할테니까 같이 공부하기
Swift - 혼자 공부하면 분명히 안할테니까 같이 공부하기
Suyeol Jeon
 
groovy databases
groovy databasesgroovy databases
groovy databases
Paul King
 
JDD2015: Where Test Doubles can lead you... - Sebastian Malaca
JDD2015: Where Test Doubles can lead you...  - Sebastian Malaca JDD2015: Where Test Doubles can lead you...  - Sebastian Malaca
JDD2015: Where Test Doubles can lead you... - Sebastian Malaca
PROIDEA
 
Approval testing from basic to advanced
Approval testing   from basic to advancedApproval testing   from basic to advanced
Approval testing from basic to advanced
Llewellyn Falco
 
Rapid and Scalable Development with MongoDB, PyMongo, and Ming
Rapid and Scalable Development with MongoDB, PyMongo, and MingRapid and Scalable Development with MongoDB, PyMongo, and Ming
Rapid and Scalable Development with MongoDB, PyMongo, and Ming
Rick Copeland
 
MongoDB With Style
MongoDB With StyleMongoDB With Style
MongoDB With Style
Gabriele Lana
 
The Ring programming language version 1.5.2 book - Part 43 of 181
The Ring programming language version 1.5.2 book - Part 43 of 181The Ring programming language version 1.5.2 book - Part 43 of 181
The Ring programming language version 1.5.2 book - Part 43 of 181
Mahmoud Samir Fayed
 
The Ring programming language version 1.5 book - Part 8 of 31
The Ring programming language version 1.5 book - Part 8 of 31The Ring programming language version 1.5 book - Part 8 of 31
The Ring programming language version 1.5 book - Part 8 of 31
Mahmoud Samir Fayed
 
The Ring programming language version 1.8 book - Part 49 of 202
The Ring programming language version 1.8 book - Part 49 of 202The Ring programming language version 1.8 book - Part 49 of 202
The Ring programming language version 1.8 book - Part 49 of 202
Mahmoud Samir Fayed
 

What's hot (20)

BDD - Behavior Driven Development Webapps mit Groovy Spock und Geb
BDD - Behavior Driven Development Webapps mit Groovy Spock und GebBDD - Behavior Driven Development Webapps mit Groovy Spock und Geb
BDD - Behavior Driven Development Webapps mit Groovy Spock und Geb
 
Spock and Geb in Action
Spock and Geb in ActionSpock and Geb in Action
Spock and Geb in Action
 
The Ring programming language version 1.9 book - Part 53 of 210
The Ring programming language version 1.9 book - Part 53 of 210The Ring programming language version 1.9 book - Part 53 of 210
The Ring programming language version 1.9 book - Part 53 of 210
 
The Ring programming language version 1.5.4 book - Part 44 of 185
The Ring programming language version 1.5.4 book - Part 44 of 185The Ring programming language version 1.5.4 book - Part 44 of 185
The Ring programming language version 1.5.4 book - Part 44 of 185
 
Map/reduce, geospatial indexing, and other cool features (Kristina Chodorow)
Map/reduce, geospatial indexing, and other cool features (Kristina Chodorow)Map/reduce, geospatial indexing, and other cool features (Kristina Chodorow)
Map/reduce, geospatial indexing, and other cool features (Kristina Chodorow)
 
Groovy puzzlers jug-moscow-part 2
Groovy puzzlers jug-moscow-part 2Groovy puzzlers jug-moscow-part 2
Groovy puzzlers jug-moscow-part 2
 
The Ring programming language version 1.3 book - Part 34 of 88
The Ring programming language version 1.3 book - Part 34 of 88The Ring programming language version 1.3 book - Part 34 of 88
The Ring programming language version 1.3 book - Part 34 of 88
 
The Ring programming language version 1.6 book - Part 46 of 189
The Ring programming language version 1.6 book - Part 46 of 189The Ring programming language version 1.6 book - Part 46 of 189
The Ring programming language version 1.6 book - Part 46 of 189
 
The Ring programming language version 1.7 book - Part 48 of 196
The Ring programming language version 1.7 book - Part 48 of 196The Ring programming language version 1.7 book - Part 48 of 196
The Ring programming language version 1.7 book - Part 48 of 196
 
The Ring programming language version 1.2 book - Part 32 of 84
The Ring programming language version 1.2 book - Part 32 of 84The Ring programming language version 1.2 book - Part 32 of 84
The Ring programming language version 1.2 book - Part 32 of 84
 
The Ring programming language version 1.4.1 book - Part 13 of 31
The Ring programming language version 1.4.1 book - Part 13 of 31The Ring programming language version 1.4.1 book - Part 13 of 31
The Ring programming language version 1.4.1 book - Part 13 of 31
 
Swift - 혼자 공부하면 분명히 안할테니까 같이 공부하기
Swift - 혼자 공부하면 분명히 안할테니까 같이 공부하기Swift - 혼자 공부하면 분명히 안할테니까 같이 공부하기
Swift - 혼자 공부하면 분명히 안할테니까 같이 공부하기
 
groovy databases
groovy databasesgroovy databases
groovy databases
 
JDD2015: Where Test Doubles can lead you... - Sebastian Malaca
JDD2015: Where Test Doubles can lead you...  - Sebastian Malaca JDD2015: Where Test Doubles can lead you...  - Sebastian Malaca
JDD2015: Where Test Doubles can lead you... - Sebastian Malaca
 
Approval testing from basic to advanced
Approval testing   from basic to advancedApproval testing   from basic to advanced
Approval testing from basic to advanced
 
Rapid and Scalable Development with MongoDB, PyMongo, and Ming
Rapid and Scalable Development with MongoDB, PyMongo, and MingRapid and Scalable Development with MongoDB, PyMongo, and Ming
Rapid and Scalable Development with MongoDB, PyMongo, and Ming
 
MongoDB With Style
MongoDB With StyleMongoDB With Style
MongoDB With Style
 
The Ring programming language version 1.5.2 book - Part 43 of 181
The Ring programming language version 1.5.2 book - Part 43 of 181The Ring programming language version 1.5.2 book - Part 43 of 181
The Ring programming language version 1.5.2 book - Part 43 of 181
 
The Ring programming language version 1.5 book - Part 8 of 31
The Ring programming language version 1.5 book - Part 8 of 31The Ring programming language version 1.5 book - Part 8 of 31
The Ring programming language version 1.5 book - Part 8 of 31
 
The Ring programming language version 1.8 book - Part 49 of 202
The Ring programming language version 1.8 book - Part 49 of 202The Ring programming language version 1.8 book - Part 49 of 202
The Ring programming language version 1.8 book - Part 49 of 202
 

Viewers also liked

Scenari introduzione Application Service Governance in Azienda
Scenari introduzione Application Service Governance in AziendaScenari introduzione Application Service Governance in Azienda
Scenari introduzione Application Service Governance in Azienda
Consulthinkspa
 
Untitled Presentation
Untitled PresentationUntitled Presentation
Untitled Presentation
Julio Llanos
 
Unesco
Unesco Unesco
Unesco
Niezzy Maggan
 
Mutuelle Senior
Mutuelle SeniorMutuelle Senior
Mutuelle Senior
Mutuelleseniors
 
Charter Broker Revenue while working and Decrease since leaving company
Charter Broker Revenue while working and Decrease since leaving companyCharter Broker Revenue while working and Decrease since leaving company
Charter Broker Revenue while working and Decrease since leaving company
Christian Borchardt
 
Final opinion
Final opinionFinal opinion
Final opinion
재경 이
 
토토추천 《∥》Too93.com《∥》토토추천 토토추천
토토추천 《∥》Too93.com《∥》토토추천 토토추천토토추천 《∥》Too93.com《∥》토토추천 토토추천
토토추천 《∥》Too93.com《∥》토토추천 토토추천
sdgdfgfdh
 
RTBI_Impact report_Final_PDF file
RTBI_Impact report_Final_PDF fileRTBI_Impact report_Final_PDF file
RTBI_Impact report_Final_PDF file
Parasuram K
 
Presentació Sara
Presentació SaraPresentació Sara
Presentació Sara
Sara NiPo
 
Infografik european hydropower-2015
Infografik european hydropower-2015Infografik european hydropower-2015
Infografik european hydropower-2015
Aquila Capital
 
Métodos Sistema Diédrico
Métodos Sistema DiédricoMétodos Sistema Diédrico
Métodos Sistema Diédrico
Elena Tarazona
 
Factsheet: TWT Inxmail-Connect für FirstSpirit™
Factsheet: TWT Inxmail-Connect für FirstSpirit™Factsheet: TWT Inxmail-Connect für FirstSpirit™
Factsheet: TWT Inxmail-Connect für FirstSpirit™
TWT
 
Presentación1
Presentación1Presentación1
Presentación1
lauriteacher
 
Mahmoud CV 2015
Mahmoud CV 2015Mahmoud CV 2015
Mahmoud CV 2015
Mahmoud Al Khazendar
 
Circles
CirclesCircles
Coordinate geometry
Coordinate geometryCoordinate geometry
Coordinate geometry
Erlyn Geronimo
 

Viewers also liked (16)

Scenari introduzione Application Service Governance in Azienda
Scenari introduzione Application Service Governance in AziendaScenari introduzione Application Service Governance in Azienda
Scenari introduzione Application Service Governance in Azienda
 
Untitled Presentation
Untitled PresentationUntitled Presentation
Untitled Presentation
 
Unesco
Unesco Unesco
Unesco
 
Mutuelle Senior
Mutuelle SeniorMutuelle Senior
Mutuelle Senior
 
Charter Broker Revenue while working and Decrease since leaving company
Charter Broker Revenue while working and Decrease since leaving companyCharter Broker Revenue while working and Decrease since leaving company
Charter Broker Revenue while working and Decrease since leaving company
 
Final opinion
Final opinionFinal opinion
Final opinion
 
토토추천 《∥》Too93.com《∥》토토추천 토토추천
토토추천 《∥》Too93.com《∥》토토추천 토토추천토토추천 《∥》Too93.com《∥》토토추천 토토추천
토토추천 《∥》Too93.com《∥》토토추천 토토추천
 
RTBI_Impact report_Final_PDF file
RTBI_Impact report_Final_PDF fileRTBI_Impact report_Final_PDF file
RTBI_Impact report_Final_PDF file
 
Presentació Sara
Presentació SaraPresentació Sara
Presentació Sara
 
Infografik european hydropower-2015
Infografik european hydropower-2015Infografik european hydropower-2015
Infografik european hydropower-2015
 
Métodos Sistema Diédrico
Métodos Sistema DiédricoMétodos Sistema Diédrico
Métodos Sistema Diédrico
 
Factsheet: TWT Inxmail-Connect für FirstSpirit™
Factsheet: TWT Inxmail-Connect für FirstSpirit™Factsheet: TWT Inxmail-Connect für FirstSpirit™
Factsheet: TWT Inxmail-Connect für FirstSpirit™
 
Presentación1
Presentación1Presentación1
Presentación1
 
Mahmoud CV 2015
Mahmoud CV 2015Mahmoud CV 2015
Mahmoud CV 2015
 
Circles
CirclesCircles
Circles
 
Coordinate geometry
Coordinate geometryCoordinate geometry
Coordinate geometry
 

Similar to 4Developers 2015: Testowanie ze Spockiem - Dominik Przybysz

Kotlin class
Kotlin classKotlin class
Kotlin class
Myeongin Woo
 
From java to kotlin beyond alt+shift+cmd+k
From java to kotlin beyond alt+shift+cmd+kFrom java to kotlin beyond alt+shift+cmd+k
From java to kotlin beyond alt+shift+cmd+k
Fabio Collini
 
Kotlin Overview (PT-BR)
Kotlin Overview (PT-BR)Kotlin Overview (PT-BR)
Kotlin Overview (PT-BR)
ThomasHorta
 
C# 6.0
C# 6.0C# 6.0
C# 6.0
Can BAKIR
 
Automatically Spotting Cross-language Relations
Automatically Spotting Cross-language RelationsAutomatically Spotting Cross-language Relations
Automatically Spotting Cross-language Relations
Federico Tomassetti
 
Scala in a Java 8 World
Scala in a Java 8 WorldScala in a Java 8 World
Scala in a Java 8 World
Daniel Blyth
 
Scala taxonomy
Scala taxonomyScala taxonomy
Scala taxonomy
Radim Pavlicek
 
つくってあそぼ Kotlin DSL ~拡張編~
つくってあそぼ Kotlin DSL ~拡張編~つくってあそぼ Kotlin DSL ~拡張編~
つくってあそぼ Kotlin DSL ~拡張編~
kamedon39
 
No excuses, switch to kotlin
No excuses, switch to kotlinNo excuses, switch to kotlin
No excuses, switch to kotlin
Thijs Suijten
 
CodeCamp Iasi 10 march 2012 - Practical Groovy
CodeCamp Iasi 10 march 2012 - Practical GroovyCodeCamp Iasi 10 march 2012 - Practical Groovy
CodeCamp Iasi 10 march 2012 - Practical Groovy
Codecamp Romania
 
Kotlin for Android Developers - Victor Kropp - Codemotion Rome 2018
Kotlin for Android Developers - Victor Kropp - Codemotion Rome 2018Kotlin for Android Developers - Victor Kropp - Codemotion Rome 2018
Kotlin for Android Developers - Victor Kropp - Codemotion Rome 2018
Codemotion
 
つくってあそぼ Kotlin DSL 第2版
つくってあそぼ Kotlin DSL 第2版つくってあそぼ Kotlin DSL 第2版
つくってあそぼ Kotlin DSL 第2版
kamedon39
 
Functional Scala 2020
Functional Scala 2020Functional Scala 2020
Functional Scala 2020
Alexander Ioffe
 
Groovy puzzlers по русски с Joker 2014
Groovy puzzlers по русски с Joker 2014Groovy puzzlers по русски с Joker 2014
Groovy puzzlers по русски с Joker 2014
Baruch Sadogursky
 
Grails Launchpad - From Ground Zero to Orbit
Grails Launchpad - From Ground Zero to OrbitGrails Launchpad - From Ground Zero to Orbit
Grails Launchpad - From Ground Zero to Orbit
Zachary Klein
 
Best of build 2021 - C# 10 & .NET 6
Best of build 2021 -  C# 10 & .NET 6Best of build 2021 -  C# 10 & .NET 6
Best of build 2021 - C# 10 & .NET 6
Moaid Hathot
 
C# 3.5 Features
C# 3.5 FeaturesC# 3.5 Features
The Groovy Puzzlers – The Complete 01 and 02 Seasons
The Groovy Puzzlers – The Complete 01 and 02 SeasonsThe Groovy Puzzlers – The Complete 01 and 02 Seasons
The Groovy Puzzlers – The Complete 01 and 02 Seasons
Baruch Sadogursky
 
Introduzione a C#
Introduzione a C#Introduzione a C#
Introduzione a C#
Lorenz Cuno Klopfenstein
 
Hey Kotlin, How it works?
Hey Kotlin, How it works?Hey Kotlin, How it works?
Hey Kotlin, How it works?
Chang W. Doh
 

Similar to 4Developers 2015: Testowanie ze Spockiem - Dominik Przybysz (20)

Kotlin class
Kotlin classKotlin class
Kotlin class
 
From java to kotlin beyond alt+shift+cmd+k
From java to kotlin beyond alt+shift+cmd+kFrom java to kotlin beyond alt+shift+cmd+k
From java to kotlin beyond alt+shift+cmd+k
 
Kotlin Overview (PT-BR)
Kotlin Overview (PT-BR)Kotlin Overview (PT-BR)
Kotlin Overview (PT-BR)
 
C# 6.0
C# 6.0C# 6.0
C# 6.0
 
Automatically Spotting Cross-language Relations
Automatically Spotting Cross-language RelationsAutomatically Spotting Cross-language Relations
Automatically Spotting Cross-language Relations
 
Scala in a Java 8 World
Scala in a Java 8 WorldScala in a Java 8 World
Scala in a Java 8 World
 
Scala taxonomy
Scala taxonomyScala taxonomy
Scala taxonomy
 
つくってあそぼ Kotlin DSL ~拡張編~
つくってあそぼ Kotlin DSL ~拡張編~つくってあそぼ Kotlin DSL ~拡張編~
つくってあそぼ Kotlin DSL ~拡張編~
 
No excuses, switch to kotlin
No excuses, switch to kotlinNo excuses, switch to kotlin
No excuses, switch to kotlin
 
CodeCamp Iasi 10 march 2012 - Practical Groovy
CodeCamp Iasi 10 march 2012 - Practical GroovyCodeCamp Iasi 10 march 2012 - Practical Groovy
CodeCamp Iasi 10 march 2012 - Practical Groovy
 
Kotlin for Android Developers - Victor Kropp - Codemotion Rome 2018
Kotlin for Android Developers - Victor Kropp - Codemotion Rome 2018Kotlin for Android Developers - Victor Kropp - Codemotion Rome 2018
Kotlin for Android Developers - Victor Kropp - Codemotion Rome 2018
 
つくってあそぼ Kotlin DSL 第2版
つくってあそぼ Kotlin DSL 第2版つくってあそぼ Kotlin DSL 第2版
つくってあそぼ Kotlin DSL 第2版
 
Functional Scala 2020
Functional Scala 2020Functional Scala 2020
Functional Scala 2020
 
Groovy puzzlers по русски с Joker 2014
Groovy puzzlers по русски с Joker 2014Groovy puzzlers по русски с Joker 2014
Groovy puzzlers по русски с Joker 2014
 
Grails Launchpad - From Ground Zero to Orbit
Grails Launchpad - From Ground Zero to OrbitGrails Launchpad - From Ground Zero to Orbit
Grails Launchpad - From Ground Zero to Orbit
 
Best of build 2021 - C# 10 & .NET 6
Best of build 2021 -  C# 10 & .NET 6Best of build 2021 -  C# 10 & .NET 6
Best of build 2021 - C# 10 & .NET 6
 
C# 3.5 Features
C# 3.5 FeaturesC# 3.5 Features
C# 3.5 Features
 
The Groovy Puzzlers – The Complete 01 and 02 Seasons
The Groovy Puzzlers – The Complete 01 and 02 SeasonsThe Groovy Puzzlers – The Complete 01 and 02 Seasons
The Groovy Puzzlers – The Complete 01 and 02 Seasons
 
Introduzione a C#
Introduzione a C#Introduzione a C#
Introduzione a C#
 
Hey Kotlin, How it works?
Hey Kotlin, How it works?Hey Kotlin, How it works?
Hey Kotlin, How it works?
 

Recently uploaded

Graspan: A Big Data System for Big Code Analysis
Graspan: A Big Data System for Big Code AnalysisGraspan: A Big Data System for Big Code Analysis
Graspan: A Big Data System for Big Code Analysis
Aftab Hussain
 
A Study of Variable-Role-based Feature Enrichment in Neural Models of Code
A Study of Variable-Role-based Feature Enrichment in Neural Models of CodeA Study of Variable-Role-based Feature Enrichment in Neural Models of Code
A Study of Variable-Role-based Feature Enrichment in Neural Models of Code
Aftab Hussain
 
openEuler Case Study - The Journey to Supply Chain Security
openEuler Case Study - The Journey to Supply Chain SecurityopenEuler Case Study - The Journey to Supply Chain Security
openEuler Case Study - The Journey to Supply Chain Security
Shane Coughlan
 
Atelier - Innover avec l’IA Générative et les graphes de connaissances
Atelier - Innover avec l’IA Générative et les graphes de connaissancesAtelier - Innover avec l’IA Générative et les graphes de connaissances
Atelier - Innover avec l’IA Générative et les graphes de connaissances
Neo4j
 
Neo4j - Product Vision and Knowledge Graphs - GraphSummit Paris
Neo4j - Product Vision and Knowledge Graphs - GraphSummit ParisNeo4j - Product Vision and Knowledge Graphs - GraphSummit Paris
Neo4j - Product Vision and Knowledge Graphs - GraphSummit Paris
Neo4j
 
UI5con 2024 - Boost Your Development Experience with UI5 Tooling Extensions
UI5con 2024 - Boost Your Development Experience with UI5 Tooling ExtensionsUI5con 2024 - Boost Your Development Experience with UI5 Tooling Extensions
UI5con 2024 - Boost Your Development Experience with UI5 Tooling Extensions
Peter Muessig
 
GOING AOT WITH GRAALVM FOR SPRING BOOT (SPRING IO)
GOING AOT WITH GRAALVM FOR  SPRING BOOT (SPRING IO)GOING AOT WITH GRAALVM FOR  SPRING BOOT (SPRING IO)
GOING AOT WITH GRAALVM FOR SPRING BOOT (SPRING IO)
Alina Yurenko
 
Revolutionizing Visual Effects Mastering AI Face Swaps.pdf
Revolutionizing Visual Effects Mastering AI Face Swaps.pdfRevolutionizing Visual Effects Mastering AI Face Swaps.pdf
Revolutionizing Visual Effects Mastering AI Face Swaps.pdf
Undress Baby
 
E-commerce Development Services- Hornet Dynamics
E-commerce Development Services- Hornet DynamicsE-commerce Development Services- Hornet Dynamics
E-commerce Development Services- Hornet Dynamics
Hornet Dynamics
 
GraphSummit Paris - The art of the possible with Graph Technology
GraphSummit Paris - The art of the possible with Graph TechnologyGraphSummit Paris - The art of the possible with Graph Technology
GraphSummit Paris - The art of the possible with Graph Technology
Neo4j
 
socradar-q1-2024-aviation-industry-report.pdf
socradar-q1-2024-aviation-industry-report.pdfsocradar-q1-2024-aviation-industry-report.pdf
socradar-q1-2024-aviation-industry-report.pdf
SOCRadar
 
Energy consumption of Database Management - Florina Jonuzi
Energy consumption of Database Management - Florina JonuziEnergy consumption of Database Management - Florina Jonuzi
Energy consumption of Database Management - Florina Jonuzi
Green Software Development
 
APIs for Browser Automation (MoT Meetup 2024)
APIs for Browser Automation (MoT Meetup 2024)APIs for Browser Automation (MoT Meetup 2024)
APIs for Browser Automation (MoT Meetup 2024)
Boni García
 
原版定制美国纽约州立大学奥尔巴尼分校毕业证学位证书原版一模一样
原版定制美国纽约州立大学奥尔巴尼分校毕业证学位证书原版一模一样原版定制美国纽约州立大学奥尔巴尼分校毕业证学位证书原版一模一样
原版定制美国纽约州立大学奥尔巴尼分校毕业证学位证书原版一模一样
mz5nrf0n
 
LORRAINE ANDREI_LEQUIGAN_HOW TO USE ZOOM
LORRAINE ANDREI_LEQUIGAN_HOW TO USE ZOOMLORRAINE ANDREI_LEQUIGAN_HOW TO USE ZOOM
LORRAINE ANDREI_LEQUIGAN_HOW TO USE ZOOM
lorraineandreiamcidl
 
Why Mobile App Regression Testing is Critical for Sustained Success_ A Detail...
Why Mobile App Regression Testing is Critical for Sustained Success_ A Detail...Why Mobile App Regression Testing is Critical for Sustained Success_ A Detail...
Why Mobile App Regression Testing is Critical for Sustained Success_ A Detail...
kalichargn70th171
 
SWEBOK and Education at FUSE Okinawa 2024
SWEBOK and Education at FUSE Okinawa 2024SWEBOK and Education at FUSE Okinawa 2024
SWEBOK and Education at FUSE Okinawa 2024
Hironori Washizaki
 
Orion Context Broker introduction 20240604
Orion Context Broker introduction 20240604Orion Context Broker introduction 20240604
Orion Context Broker introduction 20240604
Fermin Galan
 
Webinar On-Demand: Using Flutter for Embedded
Webinar On-Demand: Using Flutter for EmbeddedWebinar On-Demand: Using Flutter for Embedded
Webinar On-Demand: Using Flutter for Embedded
ICS
 
Mobile App Development Company In Noida | Drona Infotech
Mobile App Development Company In Noida | Drona InfotechMobile App Development Company In Noida | Drona Infotech
Mobile App Development Company In Noida | Drona Infotech
Drona Infotech
 

Recently uploaded (20)

Graspan: A Big Data System for Big Code Analysis
Graspan: A Big Data System for Big Code AnalysisGraspan: A Big Data System for Big Code Analysis
Graspan: A Big Data System for Big Code Analysis
 
A Study of Variable-Role-based Feature Enrichment in Neural Models of Code
A Study of Variable-Role-based Feature Enrichment in Neural Models of CodeA Study of Variable-Role-based Feature Enrichment in Neural Models of Code
A Study of Variable-Role-based Feature Enrichment in Neural Models of Code
 
openEuler Case Study - The Journey to Supply Chain Security
openEuler Case Study - The Journey to Supply Chain SecurityopenEuler Case Study - The Journey to Supply Chain Security
openEuler Case Study - The Journey to Supply Chain Security
 
Atelier - Innover avec l’IA Générative et les graphes de connaissances
Atelier - Innover avec l’IA Générative et les graphes de connaissancesAtelier - Innover avec l’IA Générative et les graphes de connaissances
Atelier - Innover avec l’IA Générative et les graphes de connaissances
 
Neo4j - Product Vision and Knowledge Graphs - GraphSummit Paris
Neo4j - Product Vision and Knowledge Graphs - GraphSummit ParisNeo4j - Product Vision and Knowledge Graphs - GraphSummit Paris
Neo4j - Product Vision and Knowledge Graphs - GraphSummit Paris
 
UI5con 2024 - Boost Your Development Experience with UI5 Tooling Extensions
UI5con 2024 - Boost Your Development Experience with UI5 Tooling ExtensionsUI5con 2024 - Boost Your Development Experience with UI5 Tooling Extensions
UI5con 2024 - Boost Your Development Experience with UI5 Tooling Extensions
 
GOING AOT WITH GRAALVM FOR SPRING BOOT (SPRING IO)
GOING AOT WITH GRAALVM FOR  SPRING BOOT (SPRING IO)GOING AOT WITH GRAALVM FOR  SPRING BOOT (SPRING IO)
GOING AOT WITH GRAALVM FOR SPRING BOOT (SPRING IO)
 
Revolutionizing Visual Effects Mastering AI Face Swaps.pdf
Revolutionizing Visual Effects Mastering AI Face Swaps.pdfRevolutionizing Visual Effects Mastering AI Face Swaps.pdf
Revolutionizing Visual Effects Mastering AI Face Swaps.pdf
 
E-commerce Development Services- Hornet Dynamics
E-commerce Development Services- Hornet DynamicsE-commerce Development Services- Hornet Dynamics
E-commerce Development Services- Hornet Dynamics
 
GraphSummit Paris - The art of the possible with Graph Technology
GraphSummit Paris - The art of the possible with Graph TechnologyGraphSummit Paris - The art of the possible with Graph Technology
GraphSummit Paris - The art of the possible with Graph Technology
 
socradar-q1-2024-aviation-industry-report.pdf
socradar-q1-2024-aviation-industry-report.pdfsocradar-q1-2024-aviation-industry-report.pdf
socradar-q1-2024-aviation-industry-report.pdf
 
Energy consumption of Database Management - Florina Jonuzi
Energy consumption of Database Management - Florina JonuziEnergy consumption of Database Management - Florina Jonuzi
Energy consumption of Database Management - Florina Jonuzi
 
APIs for Browser Automation (MoT Meetup 2024)
APIs for Browser Automation (MoT Meetup 2024)APIs for Browser Automation (MoT Meetup 2024)
APIs for Browser Automation (MoT Meetup 2024)
 
原版定制美国纽约州立大学奥尔巴尼分校毕业证学位证书原版一模一样
原版定制美国纽约州立大学奥尔巴尼分校毕业证学位证书原版一模一样原版定制美国纽约州立大学奥尔巴尼分校毕业证学位证书原版一模一样
原版定制美国纽约州立大学奥尔巴尼分校毕业证学位证书原版一模一样
 
LORRAINE ANDREI_LEQUIGAN_HOW TO USE ZOOM
LORRAINE ANDREI_LEQUIGAN_HOW TO USE ZOOMLORRAINE ANDREI_LEQUIGAN_HOW TO USE ZOOM
LORRAINE ANDREI_LEQUIGAN_HOW TO USE ZOOM
 
Why Mobile App Regression Testing is Critical for Sustained Success_ A Detail...
Why Mobile App Regression Testing is Critical for Sustained Success_ A Detail...Why Mobile App Regression Testing is Critical for Sustained Success_ A Detail...
Why Mobile App Regression Testing is Critical for Sustained Success_ A Detail...
 
SWEBOK and Education at FUSE Okinawa 2024
SWEBOK and Education at FUSE Okinawa 2024SWEBOK and Education at FUSE Okinawa 2024
SWEBOK and Education at FUSE Okinawa 2024
 
Orion Context Broker introduction 20240604
Orion Context Broker introduction 20240604Orion Context Broker introduction 20240604
Orion Context Broker introduction 20240604
 
Webinar On-Demand: Using Flutter for Embedded
Webinar On-Demand: Using Flutter for EmbeddedWebinar On-Demand: Using Flutter for Embedded
Webinar On-Demand: Using Flutter for Embedded
 
Mobile App Development Company In Noida | Drona Infotech
Mobile App Development Company In Noida | Drona InfotechMobile App Development Company In Noida | Drona Infotech
Mobile App Development Company In Noida | Drona Infotech
 

4Developers 2015: Testowanie ze Spockiem - Dominik Przybysz

  • 3. Person.groovy @Canonical class Person { String firstName String lastName Integer age boolean isAdult() { age >= 18 } }
  • 4. PersonValidator.java Part. 1 @Component public class PersonValidator { public void validatePerson(Person person) { String firstName = person.getFirstName(); if (firstName == null || firstName.length() == 0) { throw new PersonValidationException("First name must be given"); } String lastName = person.getLastName(); if (lastName == null || lastName.length() == 0) { throw new PersonValidationException("Last name must be given"); }
  • 5. PersonValidator.java Part. 2 Integer age = person.getAge(); if (age == null){ throw new PersonValidationException("Age must be given"); } if( age < 0) { throw new PersonValidationException("Age cannot be negative"); } } }
  • 6. PersonValidationException.java public class PersonValidationException extends RuntimeException { public PersonValidationException(String message) { super(message); } }
  • 7. PersonDao.groovy Part. 1 @Component class PersonDao { final JdbcTemplate jdbcTemplate @Autowired PersonDao(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate }
  • 8. PersonDao.groovy Part. 2 void persist(List<Person> persons) { persons.each { persist(it) } } @Transactional void persist(Person person) { jdbcTemplate.execute("Insert into person (first_name, last_name, age) values ('${person. firstName}', '${person.lastName}', ${person.age})") }
  • 9. PersonDao.groovy Part. 3 List<Person> findByLastName(String lastName) { jdbcTemplate.queryForList("select first_name, last_name, age from person where last_name = ?", [lastName] as Object[]) .collect({Map row -> new Person(row.first_name, row.last_name, row.age) }) } void close() { println "Closing person dao" } }
  • 10. PersonController.groovy Part. 1 @Component class PersonController { final PersonValidator personValidator final PersonDao personDao @Autowired PersonController(PersonValidator personValidator, PersonDao personDao) { this.personValidator = personValidator this.personDao = personDao }
  • 11. PersonController.groovy Part. 2 void addPerson(Person person) { personValidator.validatePerson(person) personDao.persist(person) } }
  • 12. PersonContextConfiguration.groovy Part. 1 @Configuration @ComponentScan("com.blogspot.przybyszd.spock") class PersonContextConfiguration { @Bean JdbcTemplate getJdbcTemplate(DataSource dataSource){ return new JdbcTemplate(dataSource) }
  • 13. PersonContextConfiguration.groovy Part. 2 @Bean DataSource getDataSource() { BasicDataSource basicDataSource = new BasicDataSource() basicDataSource .setDriverClassName("org.h2.Driver") basicDataSource .setUrl("jdbc:h2:mem:personDB;DB_CLOSE_DELAY=1000; INIT=runscript from 'classpath:db/person.sql';") basicDataSource.setUsername("sa") basicDataSource.setPassword("") return basicDataSource } }
  • 15. Dependencies compile 'org.codehaus.groovy:groovy-all:2.4.0' compile 'org.springframework:spring-jdbc:4.0.5.RELEASE' compile 'org.springframework:spring-beans:4.0.5.RELEASE' compile 'org.springframework:spring-context:4.0.5.RELEASE' compile 'commons-dbcp:commons-dbcp:1.4' compile 'com.h2database:h2:1.4.178' testCompile 'org.spockframework:spock-core:1.0-groovy-2.4' testCompile 'org.spockframework:spock-spring:1.0-groovy- 2.4' testCompile 'org.springframework:spring-test:4.0.5. RELEASE' testCompile 'cglib:cglib-nodep:3.1' testCompile 'org.objenesis:objenesis:2.1'
  • 16. when-then blocks class PersonTest extends Specification { def "should set first name from constructor"() { when: Person person = new Person(firstName: "Bob") then: person.firstName == "Bob" } }
  • 17. Test failed output person.firstName == "Bob" | | | | Bb false | 1 difference (66% similarity) | B(-)b | B(o)b com.blogspot.przybyszd.spock.dto.Person(Bb, null, null)
  • 18. Block with description def "should set first name from constructor 2"() { when: "person with set first name" Person person = new Person(firstName: "Bob") then: "person has first name" person.firstName == "Bob" }
  • 19. Given block def "should set first name from setter"() { given: Person person = new Person(firstName: "Bob") when: person.firstName = 'Tom' then: person.firstName == "Tom" }
  • 20. Multiple asserts def "should set person data from constructor"() { when: Person person = new Person("Bob", "Smith", 15) then: person.firstName == "Bob" person.lastName == "Smith" person.age == 15 }
  • 21. Multiple when then def "should set first name from constructor and change with setter"() { when: Person person = new Person(firstName: "Bob") then: person.firstName == "Bob" when: person.firstName = "Tom" then: person.firstName == "Tom" }
  • 22. And block def "should set first name and last name"() { when: Person person = new Person(firstName: "Bob", lastName: "Smith") then: person.firstName == "Bob" and: person.lastName == "Smith" }
  • 23. Expect block def "should compare person with equals"() { expect: new Person("Bob", "Smith", 15) == new Person ("Bob", "Smith", 15) }
  • 25. Test fields class LifecycleSpockTest extends Specification { @Shared StringWriter writer Person person }
  • 26. Setup specification def setupSpec() { println "In setup spec" writer = new StringWriter() }
  • 27. Setup each test def setup() { println "In setup" person = new Person(firstName: "Tom", lastName: "Smith", age: 21) }
  • 28. Cleanup each test def cleanup() { println "In cleanup" person = null }
  • 29. Cleanup specification def cleanupSpec() { println "In cleanup spec" writer.close() }
  • 30. Setup and clenup blocks def "should check firstName"() { setup: println "setup in test" println "should check firstName" expect: person.firstName == "Tom" cleanup: println "Cleanup after test" }
  • 31. Statements without block def "should check lastName"() { println "should check lastName" expect: person.lastName == "Smith" }
  • 33. Parameters in table @Unroll def "should set person data"() { when: Person person = new Person(lastName: lastName, firstName: firstName, age: age) then: person.firstName == firstName person.lastName == lastName person.age == age where: lastName | firstName | age "Smith" | "John" | 25 "Kowalski" | "Jan" | 24 }
  • 34. Parameters in method signature @Unroll def "should set person data 2"(String firstName, String lastName, int age) { // … where: lastName | firstName | age "Smith" | "John" | 25 "Kowalski" | "Jan" | 24 }
  • 35. Parameters in method name @Unroll def "should set person with #lastName, #firstName and #age"() { // … where: lastName | firstName | age "Smith" | "John" | 25 "Kowalski" | "Jan" | 24 }
  • 36. Call parameterless method in test name Part 1 @Unroll("should set person with #lastName.length(), #firstName.toUpperCase() and #age")
  • 37. Call parameterless method in test name Part 2 @Unroll("should set person with #lastName.length(), #firstName.toUpperCase() and #age when last name starts with #firstLetter") def "should set person with lastName, firstName and age 3"() { //… where: lastName | firstName | age "Smith" | "John" | 25 "Kowalski" | "Jan" | 24 firstLetter = lastName.charAt(0) }
  • 38. Separeted table @Unroll def "should check if person is adult with table"() { expect: new Person(age: age).isAdult() == adult where: age || adult 17 || false 18 || true 19 || true }
  • 39. Parameters from list @Unroll def "should check if person is adult with list"() { expect: new Person(age: age).isAdult() == adult ageSquare == age * age where: age << [17, 18, 19] adult << [false, true, true] ageSquare = age * age }
  • 40. Parameters from list of list @Unroll def "should check if person is adult with list 2"() { expect: new Person(age: age).isAdult() == adult where: [age, adult] << [[17,false], [18,true], [19, true]] }
  • 41. One paramter table @Unroll def "should set first name"() { when: Person person = new Person(firstName: firstName) then: person.firstName == firstName where: firstName | _ "John" | _ "Jan" | _ }
  • 42. Parameters from db - setup static Sql sql = Sql.newInstance("jdbc:h2:mem:", "sa", "", "org.h2.Driver") def setupSpec() { sql.execute("""DROP TABLE IF EXISTS person; CREATE TABLE person ( first_name VARCHAR(256) NOT NULL, last_name VARCHAR(256) NOT NULL, age INT NOT NULL );""") sql.executeInsert("""INSERT INTO person (first_name, last_name, age) VALUES ('Tom', 'Smith', 24), ('Jan', 'Kowalski', 30);""") }
  • 43. Parameters from db - cleanup def cleanupSpec() { sql.close() }
  • 44. All parameters from db @Unroll def "should set person data with #lastName, #firstName and #age"() { // … where: [firstName, lastName, age] << sql.rows("SELECT * FROM person;") }
  • 45. All parameters from db by name @Unroll def "should set person data with #lastName, #firstName and #age"() { // … where: [firstName, lastName, age] << sql.rows("SELECT first_name, last_name, age FROM person;") }
  • 46. Drop last parameter @Unroll def "should set person data with #lastName, #firstName and #age"() { // … where: [firstName, lastName] << sql.rows("SELECT * FROM person;") }
  • 47. Omit parameter @Unroll def "should set person data with #lastName, #firstName and #age"() { // … where: [_, lastName, age] << sql.rows("SELECT * FROM person;") }
  • 49. Not thrown exception PersonValidator sut = new PersonValidator() def "should pass validation"() { given: Person person = new Person(firstName: "Tom", lastName: "Smith", age: 30) when: sut.validatePerson(person) then: notThrown(PersonValidationException) }
  • 50. Not thrown exception - fails Expected no exception of type 'com.blogspot.przybyszd. spock.bean.PersonValidationException' to be thrown, but got it nevertheless
  • 51. Thrown exception @Unroll def "should not pass validation"() { then: PersonValidationException exception = thrown (PersonValidationException) exception.message == message where: firstName | lastName | age | message "Tom" | "Smith" | -1 | "Age cannot be negative" "" | "Kowalski" | 19 | "First name must be given" "Jan" | null | 19 | "Last name must be given" }
  • 52. Thrown exception - another exception Expected exception of type 'com.blogspot.przybyszd. spock.bean.PersonValidationException', but got 'java. lang.RuntimeException'
  • 53. Thrown exception - but no exception Expected exception of type 'com.blogspot.przybyszd. spock.bean.PersonValidationException', but no exception was thrown
  • 55. Creating mock JdbcTemplate jdbcTemplate = Mock(JdbcTemplate) PersonDao sut = new PersonDao(jdbcTemplate)
  • 56. Validate mock calls def "should persist one person"() { given: Person person = new Person("John", "Smith", 20) when: sut.persist(person) then: 1 * jdbcTemplate.execute("Insert into person (first_name, last_name, age) values ('John', 'Smith', 20)") }
  • 57. Mock not called Too few invocations for: 1 * jdbcTemplate.execute("Insert into person (first_name, last_name, age) values ('John', 'Smith', 20)") (0 invocations) Unmatched invocations (ordered by similarity): None
  • 58. Too many calls 1 * jdbcTemplate.execute("Insert into person (first_name, last_name, age) values ('John', 'Smith', 20)") (2 invocations) Matching invocations (ordered by last occurrence): 2 * jdbcTemplate.execute('Insert into person (first_name, last_name, age) values ('John', 'Smith', 20)') <-- this triggered the error
  • 59. Another parameters in calls def "should persist many persons"() { given: List<Person> persons = [new Person("John", "Smith", 20), new Person("Jan", "Kowalski", 15)] when: sut.persist(persons) then: 1 * jdbcTemplate.execute("Insert into person (first_name, last_name, age) values ('John', 'Smith', 20)") 1 * jdbcTemplate.execute("Insert into person (first_name, last_name, age) values ('Jan', 'Kowalski', 15)") }
  • 60. Any parameter then: 2 * jdbcTemplate.execute(_)
  • 61. Range of calls then: (1..3) * jdbcTemplate.execute(_)
  • 62. At least one call then: (1.._) * jdbcTemplate.execute(_)
  • 63. Any amount of calls then: _ * jdbcTemplate.execute(_)
  • 64. Two calls of method of any mock then: 2 * _.execute(_)
  • 65. Two calls of any method of mock then: 2 * jdbcTemplate._(_)
  • 66. Two calls of method by regex then: 2 * jdbcTemplate./exe.*/(_)
  • 67. Closure validates call then: 2 * jdbcTemplate.execute({ String sql -> sql.endsWith("('John', 'Smith', 20)") || sql.endsWith("('Jan', 'Kowalski', 15)") })
  • 68. Sequential calls def "should persist many persons in order"() { given: List<Person> persons = [new Person("John", "Smith", 20), new Person("Jan", "Kowalski", 15)] when: sut.persist(persons) then: 1 * jdbcTemplate.execute("Insert into person (first_name, last_name, age) values ('John', 'Smith', 20)") then: 1 * jdbcTemplate.execute("Insert into person (first_name, last_name, age) values ('Jan', 'Kowalski', 15)") }
  • 69. Define mock interactions in given block given: jdbcTemplate = Mock(JdbcTemplate) { 2 * execute({ String sql -> sql.endsWith("('John', 'Smith', 20)") || sql.endsWith("('Jan', 'Kowalski', 15)") }) }
  • 70. Stub def "should find one person"() { given: jdbcTemplate.queryForList("select first_name, last_name, age from person where last_name = ?", ["Kowalski"]) >> [[first_name: "Jan", last_name: "Kowalski", age: 20]] expect: sut.findByLastName("Kowalski") == [new Person ("Jan", "Kowalski", 20)] }
  • 71. Stub in context given: jdbcTemplate = Stub(JdbcTemplate) { queryForList("select first_name, last_name, age from person where last_name = ?", ["Kowalski"]) >> [[first_name: "Jan", last_name: "Kowalski", age: 20]] }
  • 72. Any stub parameters def "should find many times person"() { given: jdbcTemplate.queryForList(_, _) >> [[first_name: "Jan", last_name: "Kowalski", age: 20]] expect: sut.findByLastName("Kowalski") == [new Person ("Jan", "Kowalski", 20)] sut.findByLastName("Kowalski") == [new Person ("Jan", "Kowalski", 20)] }
  • 73. Multiple return values def "should find many times person 2"() { given: jdbcTemplate.queryForList(_, _) >> [[first_name: "Jan", last_name: "Kowalski", age: 20]] >> [[first_name: "Jan", last_name: "Kowalski", age: 25]] expect: sut.findByLastName("Kowalski") == [new Person ("Jan", "Kowalski", 20)] sut.findByLastName("Kowalski") == [new Person ("Jan", "Kowalski", 25)] }
  • 74. Multiple return values as list def "should find many times person 3"() { given: jdbcTemplate.queryForList(_, _) >>> [ [[first_name: "Jan", last_name: "Kowalski", age: 20]], [[first_name: "Jan", last_name: "Kowalski", age: 15]]] expect: sut.findByLastName("Kowalski") == [new Person ("Jan", "Kowalski", 20)] sut.findByLastName("Kowalski") == [new Person ("Jan", "Kowalski", 15)] }
  • 75. Side effects def "should throw exception on second find"() { given: jdbcTemplate.queryForList(_, _) >> [[first_name: "Jan", last_name: "Kowalski", age: 20]] >> { throw new DataRetrievalFailureException("Cannot retrieve data") } expect: sut.findByLastName("Kowalski") == [new Person ("Jan", "Kowalski", 20)] when: sut.findByLastName("Kowalski") then: thrown(DataAccessException) }
  • 76. Mocking and stubbing def "should find one person and check invocation"() { when: List result = sut.findByLastName("Kowalski") then: result == [new Person("Jan", "Kowalski", 20)] 1 * jdbcTemplate.queryForList(_, _) >> [[first_name: "Jan", last_name: "Kowalski", age: 20]] }
  • 77. Any parameter list then: 1 * jdbcTemplate.queryForList(*_) >> [[first_name: "Jan", last_name: "Kowalski", age: 20]]
  • 78. Validate parameter value then: 1 * jdbcTemplate.queryForList(_, !(["Smith"] as Object[])) >> [[first_name: "Jan", last_name: "Kowalski", age: 20]]
  • 79. Validate parameter is not null then: 1 * jdbcTemplate.queryForList(!null, _) >> [[first_name: "Jan", last_name: "Kowalski", age: 20]]
  • 80. Interaction block def "should find one person and check invocation external with first parameter not null"() { when: List result = sut.findByLastName("Kowalski") then: result == [new Person("Jan", "Kowalski", 20)] interaction { queryForListCalledOnceWithFirstName() } } void queryForListCalledOnceWithFirstName(){ 1 * jdbcTemplate.queryForList(!null, _) >> [[first_name: "Jan", last_name: "Kowalski", age: 20]] }
  • 81. Spies List sut = Spy(ArrayList, constructorArgs: [10]) def "should use spy on list"() { given: sut.add(1) >> { callRealMethod() } sut.size() >> 10 when: sut.add(1) then: sut.size() == 10 sut.get(0) == 1 }
  • 83. Spring from configuration class @ContextConfiguration(classes = PersonContextConfiguration) class PersonContextFromClassTest extends Specification { @Autowired PersonController personController @Autowired PersonDao personDao //… }
  • 84. Spring from configuration xml @ContextConfiguration(locations = "classpath: personContext.xml") class PersonContextFromXmlTest extends Specification { @Autowired PersonController personController @Autowired PersonDao personDao //… }
  • 86. Without helper def "should check person"() { when: Person result = new Person("Tom", "Smith", 20) then: result != null result.firstName == "Tom" result.lastName == "Smith" result.age == 20 }
  • 87. Boolean helper def "should check person with boolean helper method"() { when: Person result = new Person("Tom", "Smith", 20) then: checkPerson(result, "Tom", "Smith", 20) } boolean checkPerson(Person person, String firstName, String lastName, int age) { person != null && person.firstName == firstName && person.lastName == lastName && person.age == age }
  • 88. Boolean helper - output checkPerson(result, "Tom", "Smith", 20) | | false com.blogspot.przybyszd.spock.dto.Person (Tom, Smith, 20)
  • 89. Helper with assert def "should check person with assert helper method"() { when: Person result = new Person("Tom", "Smith", 20) then: checkPersonWithAssert(result, "Tom", "Smith", 20) } void checkPersonWithAssert(Person person, String firstName, String lastName, int age) { assert person != null assert person.firstName == firstName assert person.lastName == lastName assert person.age == age }
  • 90. Helper with assert - output person.firstName == "John" | | | | Tom false | 3 differences (25% similarity) | (T)o(m-) | (J)o(hn) com.blogspot.przybyszd.spock.dto.Person(Tom, Smith, 20)
  • 91. With def "should set first name, last name and age 1"() { when: Person person = new Person(firstName: "Bob", lastName: "Smith", age: 40) then: with(person) { firstName == "Bob" lastName == "Smith" age == 40 } }
  • 93. Ignore class IgnoreTest extends Specification { def "test 1"() { expect: 1 == 1 } @Ignore def "test 2"() { expect: 1 == 1 } def "test 3"() { expect: 1 == 1 } }
  • 94. IgnoreRest class IgnoreRestTest extends Specification { def "test 1"() { expect: 1 == 1 } @IgnoreRest def "test 2"() { expect: 1 == 1 } def "test 3"() { expect: 1 == 1 } }
  • 95. IgnoreIf class IgnoreIfTest extends Specification { // @IgnoreIf({os.windows}) // @IgnoreIf({os.linux}) @IgnoreIf({ System.getProperty("os.name").contains ("Linux") }) def "test 1"() { expect: 1 == 1 } }
  • 96. Requires class RequiresTest extends Specification { // @Requires({os.windows}) // @Requires({os.linux}) @Requires({ System.getProperty("os.name").contains ("windows") }) def "test 1"() { expect: 1 == 1 } }
  • 97. AutoCleanup class AutoCleanupTest extends Specification { JdbcTemplate jdbcTemplate = Mock(JdbcTemplate) @AutoCleanup(value = "close", quiet = true) PersonDao sut = new PersonDao(jdbcTemplate) def "test 1"() { expect: sut != null } }
  • 98. FailsWith class FailsWithTest extends Specification { @FailsWith(RuntimeException) def "test 1"() { expect: throw new RuntimeException() } }
  • 99. Timeout class TimeoutTest extends Specification { @Timeout(value = 750, unit = TimeUnit.MILLISECONDS) def "test 1"() { expect: 1 == 1 } }
  • 100. Title @Title("Title annotation is tested in this specification") class TitleTest extends Specification { def "test 1"() { expect: 1 == 1 } }
  • 101. Narrative @Narrative("""Multiline narrative annotation is tested in this specification""") class NarrativeTest extends Specification { def "test 1"() { expect: 1 == 1 } }
  • 102. Subject @Subject(Person) class SubjectTest extends Specification { @Subject Person person = new Person("John", "Smith", 21) def "should be adult"() { expect: person.isAdult() } }
  • 103. Issue class IssueTest extends Specification { @Issue(["http://example.org/mantis/view.php? id=12345", "http://example.org/mantis/view.php?id=23"]) def "test 1"() { expect: 1 == 1 } }
  • 104. Extra
  • 105. void instead of def void "should set first name from constructor"() { when: Person person = new Person(firstName: "Bob") then: person.firstName == "Bob" }
  • 106. Shoud instead of def Should "set first name from constructor"() { when: Person person = new Person(firstName: "Bob") then: person.firstName == "Bob" }
  • 107. Shoud instead of def - how it works import java.lang.Void as Should
  • 108. Q & A