SlideShare uma empresa Scribd logo
1 de 109
Baixar para ler 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

Mais conteúdo relacionado

Mais procurados

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
 

Mais procurados (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
 

Destaque

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
 

Destaque (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
 

Semelhante a 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
 

Semelhante a 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?
 

Último

call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️Delhi Call girls
 
Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...
Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...
Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...MyIntelliSource, Inc.
 
Diamond Application Development Crafting Solutions with Precision
Diamond Application Development Crafting Solutions with PrecisionDiamond Application Development Crafting Solutions with Precision
Diamond Application Development Crafting Solutions with PrecisionSolGuruz
 
Salesforce Certified Field Service Consultant
Salesforce Certified Field Service ConsultantSalesforce Certified Field Service Consultant
Salesforce Certified Field Service ConsultantAxelRicardoTrocheRiq
 
Test Automation Strategy for Frontend and Backend
Test Automation Strategy for Frontend and BackendTest Automation Strategy for Frontend and Backend
Test Automation Strategy for Frontend and BackendArshad QA
 
Clustering techniques data mining book ....
Clustering techniques data mining book ....Clustering techniques data mining book ....
Clustering techniques data mining book ....ShaimaaMohamedGalal
 
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
 
Hand gesture recognition PROJECT PPT.pptx
Hand gesture recognition PROJECT PPT.pptxHand gesture recognition PROJECT PPT.pptx
Hand gesture recognition PROJECT PPT.pptxbodapatigopi8531
 
5 Signs You Need a Fashion PLM Software.pdf
5 Signs You Need a Fashion PLM Software.pdf5 Signs You Need a Fashion PLM Software.pdf
5 Signs You Need a Fashion PLM Software.pdfWave PLM
 
Professional Resume Template for Software Developers
Professional Resume Template for Software DevelopersProfessional Resume Template for Software Developers
Professional Resume Template for Software DevelopersVinodh Ram
 
Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...
Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...
Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...OnePlan Solutions
 
Right Money Management App For Your Financial Goals
Right Money Management App For Your Financial GoalsRight Money Management App For Your Financial Goals
Right Money Management App For Your Financial GoalsJhone kinadey
 
Optimizing AI for immediate response in Smart CCTV
Optimizing AI for immediate response in Smart CCTVOptimizing AI for immediate response in Smart CCTV
Optimizing AI for immediate response in Smart CCTVshikhaohhpro
 
SyndBuddy AI 2k Review 2024: Revolutionizing Content Syndication with AI
SyndBuddy AI 2k Review 2024: Revolutionizing Content Syndication with AISyndBuddy AI 2k Review 2024: Revolutionizing Content Syndication with AI
SyndBuddy AI 2k Review 2024: Revolutionizing Content Syndication with AIABDERRAOUF MEHENNI
 
The Ultimate Test Automation Guide_ Best Practices and Tips.pdf
The Ultimate Test Automation Guide_ Best Practices and Tips.pdfThe Ultimate Test Automation Guide_ Best Practices and Tips.pdf
The Ultimate Test Automation Guide_ Best Practices and Tips.pdfkalichargn70th171
 
Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...
Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...
Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...harshavardhanraghave
 
Unveiling the Tech Salsa of LAMs with Janus in Real-Time Applications
Unveiling the Tech Salsa of LAMs with Janus in Real-Time ApplicationsUnveiling the Tech Salsa of LAMs with Janus in Real-Time Applications
Unveiling the Tech Salsa of LAMs with Janus in Real-Time ApplicationsAlberto González Trastoy
 
Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...
Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...
Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...kellynguyen01
 

Último (20)

call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
 
Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...
Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...
Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...
 
Diamond Application Development Crafting Solutions with Precision
Diamond Application Development Crafting Solutions with PrecisionDiamond Application Development Crafting Solutions with Precision
Diamond Application Development Crafting Solutions with Precision
 
CHEAP Call Girls in Pushp Vihar (-DELHI )🔝 9953056974🔝(=)/CALL GIRLS SERVICE
CHEAP Call Girls in Pushp Vihar (-DELHI )🔝 9953056974🔝(=)/CALL GIRLS SERVICECHEAP Call Girls in Pushp Vihar (-DELHI )🔝 9953056974🔝(=)/CALL GIRLS SERVICE
CHEAP Call Girls in Pushp Vihar (-DELHI )🔝 9953056974🔝(=)/CALL GIRLS SERVICE
 
Salesforce Certified Field Service Consultant
Salesforce Certified Field Service ConsultantSalesforce Certified Field Service Consultant
Salesforce Certified Field Service Consultant
 
Test Automation Strategy for Frontend and Backend
Test Automation Strategy for Frontend and BackendTest Automation Strategy for Frontend and Backend
Test Automation Strategy for Frontend and Backend
 
Clustering techniques data mining book ....
Clustering techniques data mining book ....Clustering techniques data mining book ....
Clustering techniques data mining book ....
 
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
 
Hand gesture recognition PROJECT PPT.pptx
Hand gesture recognition PROJECT PPT.pptxHand gesture recognition PROJECT PPT.pptx
Hand gesture recognition PROJECT PPT.pptx
 
5 Signs You Need a Fashion PLM Software.pdf
5 Signs You Need a Fashion PLM Software.pdf5 Signs You Need a Fashion PLM Software.pdf
5 Signs You Need a Fashion PLM Software.pdf
 
Professional Resume Template for Software Developers
Professional Resume Template for Software DevelopersProfessional Resume Template for Software Developers
Professional Resume Template for Software Developers
 
Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...
Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...
Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...
 
Right Money Management App For Your Financial Goals
Right Money Management App For Your Financial GoalsRight Money Management App For Your Financial Goals
Right Money Management App For Your Financial Goals
 
Optimizing AI for immediate response in Smart CCTV
Optimizing AI for immediate response in Smart CCTVOptimizing AI for immediate response in Smart CCTV
Optimizing AI for immediate response in Smart CCTV
 
SyndBuddy AI 2k Review 2024: Revolutionizing Content Syndication with AI
SyndBuddy AI 2k Review 2024: Revolutionizing Content Syndication with AISyndBuddy AI 2k Review 2024: Revolutionizing Content Syndication with AI
SyndBuddy AI 2k Review 2024: Revolutionizing Content Syndication with AI
 
The Ultimate Test Automation Guide_ Best Practices and Tips.pdf
The Ultimate Test Automation Guide_ Best Practices and Tips.pdfThe Ultimate Test Automation Guide_ Best Practices and Tips.pdf
The Ultimate Test Automation Guide_ Best Practices and Tips.pdf
 
Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...
Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...
Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...
 
Unveiling the Tech Salsa of LAMs with Janus in Real-Time Applications
Unveiling the Tech Salsa of LAMs with Janus in Real-Time ApplicationsUnveiling the Tech Salsa of LAMs with Janus in Real-Time Applications
Unveiling the Tech Salsa of LAMs with Janus in Real-Time Applications
 
Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...
Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...
Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...
 
Exploring iOS App Development: Simplifying the Process
Exploring iOS App Development: Simplifying the ProcessExploring iOS App Development: Simplifying the Process
Exploring iOS App Development: Simplifying the Process
 

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