SlideShare a Scribd company logo
1 of 109
Download to read offline
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 GebChristian 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 210Mahmoud 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 185Mahmoud 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 2Evgeny 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 88Mahmoud 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 189Mahmoud 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 196Mahmoud 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 84Mahmoud 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 31Mahmoud Samir Fayed
 
Swift - 혼자 공부하면 분명히 안할테니까 같이 공부하기
Swift - 혼자 공부하면 분명히 안할테니까 같이 공부하기Swift - 혼자 공부하면 분명히 안할테니까 같이 공부하기
Swift - 혼자 공부하면 분명히 안할테니까 같이 공부하기Suyeol Jeon
 
groovy databases
groovy databasesgroovy databases
groovy databasesPaul 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 advancedLlewellyn 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 MingRick Copeland
 
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 181Mahmoud 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 31Mahmoud 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 202Mahmoud 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 AziendaConsulthinkspa
 
Untitled Presentation
Untitled PresentationUntitled Presentation
Untitled PresentationJulio Llanos
 
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 companyChristian Borchardt
 
토토추천 《∥》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 fileParasuram K
 
Presentació Sara
Presentació SaraPresentació Sara
Presentació SaraSara NiPo
 
Infografik european hydropower-2015
Infografik european hydropower-2015Infografik european hydropower-2015
Infografik european hydropower-2015Aquila Capital
 
Métodos Sistema Diédrico
Métodos Sistema DiédricoMétodos Sistema Diédrico
Métodos Sistema DiédricoElena 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
 

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

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+kFabio Collini
 
Kotlin Overview (PT-BR)
Kotlin Overview (PT-BR)Kotlin Overview (PT-BR)
Kotlin Overview (PT-BR)ThomasHorta
 
Automatically Spotting Cross-language Relations
Automatically Spotting Cross-language RelationsAutomatically Spotting Cross-language Relations
Automatically Spotting Cross-language RelationsFederico Tomassetti
 
Scala in a Java 8 World
Scala in a Java 8 WorldScala in a Java 8 World
Scala in a Java 8 WorldDaniel Blyth
 
つくってあそぼ 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 kotlinThijs 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 GroovyCodecamp 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 2018Codemotion
 
つくってあそぼ Kotlin DSL 第2版
つくってあそぼ Kotlin DSL 第2版つくってあそぼ Kotlin DSL 第2版
つくってあそぼ Kotlin DSL 第2版kamedon39
 
Groovy puzzlers по русски с Joker 2014
Groovy puzzlers по русски с Joker 2014Groovy puzzlers по русски с Joker 2014
Groovy puzzlers по русски с Joker 2014Baruch 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 OrbitZachary 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 6Moaid Hathot
 
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 SeasonsBaruch Sadogursky
 
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

Implementing Zero Trust strategy with Azure
Implementing Zero Trust strategy with AzureImplementing Zero Trust strategy with Azure
Implementing Zero Trust strategy with AzureDinusha Kumarasiri
 
Alluxio Monthly Webinar | Cloud-Native Model Training on Distributed Data
Alluxio Monthly Webinar | Cloud-Native Model Training on Distributed DataAlluxio Monthly Webinar | Cloud-Native Model Training on Distributed Data
Alluxio Monthly Webinar | Cloud-Native Model Training on Distributed DataAlluxio, Inc.
 
ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...
ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...
ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...Christina Lin
 
Building Real-Time Data Pipelines: Stream & Batch Processing workshop Slide
Building Real-Time Data Pipelines: Stream & Batch Processing workshop SlideBuilding Real-Time Data Pipelines: Stream & Batch Processing workshop Slide
Building Real-Time Data Pipelines: Stream & Batch Processing workshop SlideChristina Lin
 
What are the features of Vehicle Tracking System?
What are the features of Vehicle Tracking System?What are the features of Vehicle Tracking System?
What are the features of Vehicle Tracking System?Watsoo Telematics
 
Adobe Marketo Engage Deep Dives: Using Webhooks to Transfer Data
Adobe Marketo Engage Deep Dives: Using Webhooks to Transfer DataAdobe Marketo Engage Deep Dives: Using Webhooks to Transfer Data
Adobe Marketo Engage Deep Dives: Using Webhooks to Transfer DataBradBedford3
 
Der Spagat zwischen BIAS und FAIRNESS (2024)
Der Spagat zwischen BIAS und FAIRNESS (2024)Der Spagat zwischen BIAS und FAIRNESS (2024)
Der Spagat zwischen BIAS und FAIRNESS (2024)OPEN KNOWLEDGE GmbH
 
The Essentials of Digital Experience Monitoring_ A Comprehensive Guide.pdf
The Essentials of Digital Experience Monitoring_ A Comprehensive Guide.pdfThe Essentials of Digital Experience Monitoring_ A Comprehensive Guide.pdf
The Essentials of Digital Experience Monitoring_ A Comprehensive Guide.pdfkalichargn70th171
 
EY_Graph Database Powered Sustainability
EY_Graph Database Powered SustainabilityEY_Graph Database Powered Sustainability
EY_Graph Database Powered SustainabilityNeo4j
 
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...soniya singh
 
Unit 1.1 Excite Part 1, class 9, cbse...
Unit 1.1 Excite Part 1, class 9, cbse...Unit 1.1 Excite Part 1, class 9, cbse...
Unit 1.1 Excite Part 1, class 9, cbse...aditisharan08
 
cybersecurity notes for mca students for learning
cybersecurity notes for mca students for learningcybersecurity notes for mca students for learning
cybersecurity notes for mca students for learningVitsRangannavar
 
Project Based Learning (A.I).pptx detail explanation
Project Based Learning (A.I).pptx detail explanationProject Based Learning (A.I).pptx detail explanation
Project Based Learning (A.I).pptx detail explanationkaushalgiri8080
 
Cloud Management Software Platforms: OpenStack
Cloud Management Software Platforms: OpenStackCloud Management Software Platforms: OpenStack
Cloud Management Software Platforms: OpenStackVICTOR MAESTRE RAMIREZ
 
XpertSolvers: Your Partner in Building Innovative Software Solutions
XpertSolvers: Your Partner in Building Innovative Software SolutionsXpertSolvers: Your Partner in Building Innovative Software Solutions
XpertSolvers: Your Partner in Building Innovative Software SolutionsMehedi Hasan Shohan
 
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...MyIntelliSource, Inc.
 
What is Fashion PLM and Why Do You Need It
What is Fashion PLM and Why Do You Need ItWhat is Fashion PLM and Why Do You Need It
What is Fashion PLM and Why Do You Need ItWave PLM
 
The Evolution of Karaoke From Analog to App.pdf
The Evolution of Karaoke From Analog to App.pdfThe Evolution of Karaoke From Analog to App.pdf
The Evolution of Karaoke From Analog to App.pdfPower Karaoke
 
why an Opensea Clone Script might be your perfect match.pdf
why an Opensea Clone Script might be your perfect match.pdfwhy an Opensea Clone Script might be your perfect match.pdf
why an Opensea Clone Script might be your perfect match.pdfjoe51371421
 
Professional Resume Template for Software Developers
Professional Resume Template for Software DevelopersProfessional Resume Template for Software Developers
Professional Resume Template for Software DevelopersVinodh Ram
 

Recently uploaded (20)

Implementing Zero Trust strategy with Azure
Implementing Zero Trust strategy with AzureImplementing Zero Trust strategy with Azure
Implementing Zero Trust strategy with Azure
 
Alluxio Monthly Webinar | Cloud-Native Model Training on Distributed Data
Alluxio Monthly Webinar | Cloud-Native Model Training on Distributed DataAlluxio Monthly Webinar | Cloud-Native Model Training on Distributed Data
Alluxio Monthly Webinar | Cloud-Native Model Training on Distributed Data
 
ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...
ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...
ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...
 
Building Real-Time Data Pipelines: Stream & Batch Processing workshop Slide
Building Real-Time Data Pipelines: Stream & Batch Processing workshop SlideBuilding Real-Time Data Pipelines: Stream & Batch Processing workshop Slide
Building Real-Time Data Pipelines: Stream & Batch Processing workshop Slide
 
What are the features of Vehicle Tracking System?
What are the features of Vehicle Tracking System?What are the features of Vehicle Tracking System?
What are the features of Vehicle Tracking System?
 
Adobe Marketo Engage Deep Dives: Using Webhooks to Transfer Data
Adobe Marketo Engage Deep Dives: Using Webhooks to Transfer DataAdobe Marketo Engage Deep Dives: Using Webhooks to Transfer Data
Adobe Marketo Engage Deep Dives: Using Webhooks to Transfer Data
 
Der Spagat zwischen BIAS und FAIRNESS (2024)
Der Spagat zwischen BIAS und FAIRNESS (2024)Der Spagat zwischen BIAS und FAIRNESS (2024)
Der Spagat zwischen BIAS und FAIRNESS (2024)
 
The Essentials of Digital Experience Monitoring_ A Comprehensive Guide.pdf
The Essentials of Digital Experience Monitoring_ A Comprehensive Guide.pdfThe Essentials of Digital Experience Monitoring_ A Comprehensive Guide.pdf
The Essentials of Digital Experience Monitoring_ A Comprehensive Guide.pdf
 
EY_Graph Database Powered Sustainability
EY_Graph Database Powered SustainabilityEY_Graph Database Powered Sustainability
EY_Graph Database Powered Sustainability
 
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...
 
Unit 1.1 Excite Part 1, class 9, cbse...
Unit 1.1 Excite Part 1, class 9, cbse...Unit 1.1 Excite Part 1, class 9, cbse...
Unit 1.1 Excite Part 1, class 9, cbse...
 
cybersecurity notes for mca students for learning
cybersecurity notes for mca students for learningcybersecurity notes for mca students for learning
cybersecurity notes for mca students for learning
 
Project Based Learning (A.I).pptx detail explanation
Project Based Learning (A.I).pptx detail explanationProject Based Learning (A.I).pptx detail explanation
Project Based Learning (A.I).pptx detail explanation
 
Cloud Management Software Platforms: OpenStack
Cloud Management Software Platforms: OpenStackCloud Management Software Platforms: OpenStack
Cloud Management Software Platforms: OpenStack
 
XpertSolvers: Your Partner in Building Innovative Software Solutions
XpertSolvers: Your Partner in Building Innovative Software SolutionsXpertSolvers: Your Partner in Building Innovative Software Solutions
XpertSolvers: Your Partner in Building Innovative Software Solutions
 
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...
 
What is Fashion PLM and Why Do You Need It
What is Fashion PLM and Why Do You Need ItWhat is Fashion PLM and Why Do You Need It
What is Fashion PLM and Why Do You Need It
 
The Evolution of Karaoke From Analog to App.pdf
The Evolution of Karaoke From Analog to App.pdfThe Evolution of Karaoke From Analog to App.pdf
The Evolution of Karaoke From Analog to App.pdf
 
why an Opensea Clone Script might be your perfect match.pdf
why an Opensea Clone Script might be your perfect match.pdfwhy an Opensea Clone Script might be your perfect match.pdf
why an Opensea Clone Script might be your perfect match.pdf
 
Professional Resume Template for Software Developers
Professional Resume Template for Software DevelopersProfessional Resume Template for Software Developers
Professional Resume Template for Software Developers
 

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