Spock
Spock
the enterprise ready specification framework
Ted Vinke, 2018
http://spockframework.org/spock/docs/1.1/index.html
• Overview

• Data Driven Testing

• Interaction Based Testing

• Extensions
class MathSpec extends Specification {
void "should find maximum of two numbers"() {
when:
def x = Math.max(1, 2)
then:
x == 2
}
}
class MathSpec extends Specification {
void "should find maximum of two numbers"() {
when:
def x = Math.max(1, 2)
then:
x == 2
}
}
class MathSpec extends Specification {
void "should find maximum of two numbers"() {
when:
def x = Math.max(1, 2)
then:
x == 2
}
}
class MathSpec extends Specification {
void "should find maximum of two numbers"() {
when:
def x = Math.max(1, 2)
then:
x == 2
}
}
setup:

when:

then:

expect:

cleanup:

where:
Setup

Stimulus

Response

Cleanup
Blocks Phases
setup:
def stack = new Stack()
def elem = "push me"
Setup Blocks
when:
stack.push(elem)
then:
!stack.empty
stack.size() == 1
stack.peek() == elem
Plain boolean conditions
Conditions
when:
stack.push(elem)
then:
!stack.empty
stack.size() == 2
stack.peek() == elem
Plain boolean conditions
Conditions
Condition not satisfied:
stack.size() == 2
| | |
| 1 false
[push me]
when:
stack.pop()
then:
thrown(EmptyStackException)
Exception conditions
Conditions
when:
stack.pop()
then:
def e = thrown(EmptyStackException)
Exception conditions
Conditions
when:
stack.pop()
then:
def e = thrown(EmptyStackException)
e.cause == null
Exception conditions
Conditions
when:
def x = Math.max(1, 2)
then:
x == 2
Expect Blocks
expect:
Math.max(1, 2) == 2
setup:
def file = new File("/some/path")
file.createNewFile()
// ...
cleanup:
file.delete()
Cleanup Blocks
Data Driven Testing
class MathSpec extends Specification {
def "maximum of two numbers"() {
expect:
Math.max(1, 3) == 3
Math.max(7, 4) == 7
Math.max(0, 0) == 0
}
}
The naive way
Data Driven Testing
• Code and data are mixed and cannot
easily be changed independently

• Data cannot easily be auto-generated or
fetched from external sources

• In order to exercise the same code
multiple times, it either has to be
duplicated or extracted into a separate
method

• In case of a failure, it may not be
immediately clear which inputs caused
the failure

• Exercising the same code multiple times
does not benefit from the same isolation
as executing separate methods does
…has potential drawbacks:
class MathSpec extends Specification {
def "maximum of two numbers"() {
expect:
Math.max(1, 3) == 3
Math.max(7, 4) == 7
Math.max(0, 0) == 0
}
}
Refactored
Data Driven Testing
class MathSpec extends Specification {
def "maximum of two numbers"() {
expect:
}
}
Refactored
Data Driven Testing
class MathSpec extends Specification {
def "maximum of two numbers"(int a, int b, int c) {
expect:
}
}
Refactored
Data Driven Testing
class MathSpec extends Specification {
def "maximum of two numbers"(int a, int b, int c) {
expect:
Math.max(a, b) == c
...
}
}
Refactored
Data Driven Testing
class MathSpec extends Specification {
def "maximum of two numbers"(int a, int b, int c) {
expect:
Math.max(a, b) == c
}
}
Data Tables
Data Driven Testing
class MathSpec extends Specification {
def "maximum of two numbers"(int a, int b, int c) {
expect:
Math.max(a, b) == c
where:
a | b | c
1 | 3 | 3
7 | 4 | 7
0 | 0 | 0
}
}
Data Tables
Data Driven Testing
class MathSpec extends Specification {
def "maximum of two numbers"() {
expect:
Math.max(a, b) == c
where:
a | b | c
1 | 3 | 3
7 | 4 | 7
0 | 0 | 0
}
}
Data Tables
Data Driven Testing
method parameters omitted
class MathSpec extends Specification {
def "maximum of two numbers"() {
expect:
Math.max(a, b) == c
where:
a | b || c
1 | 3 || 3
7 | 4 || 7
0 | 0 || 0
}
}
Data Tables
Data Driven Testing
visually seperate inputs and outputs
class MathSpec extends Specification {
def "maximum of two numbers"() {
expect:
Math.max(a, b) == c
where:
a | b || c
1 | 3 || 3
7 | 4 || 7
0 | 0 || 0
}
}
Data Tables with visually seperated inputs and outputs
Data Driven Testing
class MathSpec extends Specification {
def "maximum of two numbers"() {
expect:
Math.max(a, b) == c
where:
a | b || c
1 | 3 || 3
7 | 4 || 7
0 | 0 || 0
}
}
Reporting of failures
Data Driven Testing
maximum of two numbers FAILED
Condition not satisfied:
Math.max(a, b) == c
| | | | |
| 7 4 | 7
42 false
@Unroll
def "maximum of two numbers"()
Reporting of failures
Data Driven Testing
maximum of two numbers[0] PASSED
maximum of two numbers[1] FAILED
Math.max(a, b) == c
| | | | |
| 7 4 | 7
42 false
maximum of two numbers[2] PASSED
@Unroll
def "maximum of #a and #b is #c"()
Reporting of failures
Data Driven Testing
maximum of 3 and 5 is 5 PASSED
maximum of 7 and 4 is 7 FAILED
Math.max(a, b) == c
| | | | |
| 7 4 | 7
42 false
maximum of 0 and 0 is 0 PASSED
...
where:
a << [1, 7, 0]
b << [3, 4, 0]
c << [3, 7, 0]
Data pipes
Data Driven Testing
...
where:
a = 3
b = Math.random() * 100
c = a > b ? a : b
@Shared sql = Sql.newInstance("jdbc:h2:mem:", "org.h2.Driver")
def "maximum of two numbers"() {
expect:
Math.max(a, b) == c
where:
[a, b, c] << sql.rows("select a, b, c from maxdata")
}
Data pipes
Data Driven Testing
Interaction Based
Testing
interface Subscriber {
void receive(String message)
}
Publishers and subscribers
Interaction Based Testing
class Publisher {
List<Subscriber> subscribers = []
int messageCount = 0
void send(String message){
subscribers*.receive(message)
messageCount++
}
}
class PublisherSpec extends Specification {
Publisher publisher = new Publisher()
}
Publishers and subscribers
Interaction Based Testing
class Publisher {
List<Subscriber> subscribers = []
int messageCount = 0
void send(String message){
subscribers*.receive(message)
messageCount++
}
}
interface Subscriber {
void receive(String message)
}
class PublisherSpec extends Specification {
Publisher publisher = new Publisher()
}
}
Publishers and subscribers
Interaction Based Testing
class PublisherSpec extends Specification {
Publisher publisher = new Publisher()
Subscriber subscriber = Mock()
Subscriber subscriber2 = Mock()
}
}
Creating mocks
Interaction Based Testing
class PublisherSpec extends Specification {
Publisher publisher = new Publisher()
Subscriber subscriber = Mock()
Subscriber subscriber2 = Mock()
def setup() {
// << is a Groovy shorthand for List.add()
publisher.subscribers << subscriber
publisher.subscribers << subscriber2
}}
}
Injecting mocks
Interaction Based Testing
def "should send messages to all subscribers"() {
}
Mocking
Interaction Based Testing
def "should send messages to all subscribers"() {
when:
publisher.send("hello")
}
Mocking
Interaction Based Testing
def "should send messages to all subscribers"() {
when:
publisher.send("hello")
then:
1 * subscriber.receive("hello")
1 * subscriber2.receive("hello")
}
Mocking
Interaction Based Testing
def "should send messages to all subscribers"() {
when:
publisher.send("hello")
then:
1 * subscriber.receive("hello")
1 * subscriber2.receive("hello")
}
Mocking
Interaction Based Testing
interactions
1 * subscriber.receive("hello")
| | | |
| | | argument constraint
| | method constraint
| target constraint
cardinality
Interactions
Interaction Based Testing
1 * subscriber.receive("hello") // exactly one call
0 * subscriber.receive("hello") // zero calls
(1..3) * subscriber.receive("hello") // between one and three calls
(1.._) * subscriber.receive("hello") // at least one call
(_..3) * subscriber.receive("hello") // at most three calls
_ * subscriber.receive("hello") // any number of calls
Cardinality
Interaction Based Testing
1 * subscriber.receive("hello") // exactly one call
0 * subscriber.receive("hello") // zero calls
(1..3) * subscriber.receive("hello") // between one and three calls
(1.._) * subscriber.receive("hello") // at least one call
(_..3) * subscriber.receive("hello") // at most three calls
_ * subscriber.receive("hello") // any number of calls
Cardinality
Interaction Based Testing
1 * subscriber.receive("hello") // a call to 'subscriber'
1 * _.receive("hello") // a call to any mock object
Target Constraint
Interaction Based Testing
1 * subscriber.receive("hello") // a method named 'receive'
1 * subscriber./r.*e/("hello") // matches regular expression
Method Constraint
Interaction Based Testing
1 * subscriber.receive("hello") // an argument equal to the String "hello"
1 * subscriber.receive(!"hello") // an argument unequal to the String "hello"
1 * subscriber.receive() // the empty argument list
1 * subscriber.receive(_) // any single argument (including null)
1 * subscriber.receive(*_) // any argument list (including empty)
1 * subscriber.receive(!null) // any non-null argument
1 * subscriber.receive(_ as String) // any non-null argument that is-a String
1 * subscriber.receive({ it.size() > 3 }) // an argument that satisfies predicate
Argument Constraints
Interaction Based Testing
1 * subscriber.receive("hello") // an argument equal to the String "hello"
1 * subscriber.receive(!"hello") // an argument unequal to the String "hello"
1 * subscriber.receive() // the empty argument list
1 * subscriber.receive(_) // any single argument (including null)
1 * subscriber.receive(*_) // any argument list (including empty)
1 * subscriber.receive(!null) // any non-null argument
1 * subscriber.receive(_ as String) // any non-null argument that is-a String
1 * subscriber.receive({ it.size() > 3 }) // an argument that satisfies predicate
Argument Constraints
Interaction Based Testing
1 * process.invoke("ls", "-a", _, !null,
{ ["abcdefghiklmnopqrstuwx1"].contains(it) })
1 * subscriber._(*_)
Quiz Based Testing
1 * _
class MySpec extends Specification {
Subscriber subscriber = Mock
def setup() {
1 * subscriber.receive("hello")
1 * subscriber.receive("goodbye")
}
def "test"() {
}
}
Interactions everywhere!
class MySpec extends Specification {
Subscriber subscriber = Mock {
1 * receive("hello")
1 * receive("goodbye")
}
def "test"() {
}
}
class MySpec extends Specification {
Subscriber subscriber = Mock
def "test"() {
with(subscriber) {
1 * receive("hello")
1 * receive("goodbye")
}
}
}
when:
publisher.send("message1")
then:
1 * subscriber.receive("message1")
when:
publisher.send("message2")
then:
1 * subscriber.receive("message2")
Scope of Interactions
Interaction Based Testing
Verification of Interactions
Interaction Based Testing
Too many invocations for:
2 * subscriber.receive(_) (3 invocations)
Matching invocations (ordered by last occurrence):
2 * subscriber.receive("hello") <-- this triggered the error
1 * subscriber.receive("goodbye")
Verification of Interactions
Interaction Based Testing
Too few invocations for:
1 * subscriber.receive("hello") (0 invocations)
Unmatched invocations (ordered by similarity):
1 * subscriber.receive("goodbye")
1 * subscriber2.receive("hello")
Stubbing of interactions
Interaction Based Testing
interface Subscriber {
String receive(String message)
}
subscriber.receive(_) >> "ok"
subscriber.receive(_) >> "ok"
| | | |
| | | response generator
| | argument constraint
| method constraint
target constraint
Response generator
Interaction Based Testing
subscriber.receive(_) >> "ok"
Returning values
Interaction Based Testing
subscriber.receive("message1") >> "ok"
subscriber.receive("message2") >> "fail"
subscriber.receive(_) >>> ["ok", "error", "error", "ok"]
subscriber.receive(_) >> { String message ->
message.size() > 3 ? "ok" : "fail" }
def subscriber = Stub(Subscriber)
Other kinds of mock objects
Interaction Based Testing
Stubs
Spies
def subscriber = Spy(SubscriberImpl, constructorArgs: ["Fred"])
Extensions
@Ignore
def "my feature"() { ... }
@IgnoreIf({ System.getProperty("os.name").contains("windows") })
def "my feature"() { ... }
@Requires({ os.windows })
class MySpec extends Specification { ... }
Ignoring and requiring
Extensions
@PendingFeature
def "not implemented yet"() { ... }
Pending features
Extensions
@Timeout(5)
def "I fail if I run for more than five seconds"() { ... }
@Timeout(value = 100, unit = TimeUnit.MILLISECONDS)
def "I better be quick" { ... }
Timeouts
Extensions
@Narrative("""
As a user
I want foo
So that bar
""")
@See("http://spockframework.org/spec")
class MySpec {
@Issue("http://my.issues.org/FOO-2")
def "Foo should do bar"() { ... }
}
Documentation
Extensions
def "should send emails to matching users"() {
given:
def user1 = new User(id: 1L, userName: 'user one', email: 'test@user.one')
def user2 = new User(id: 2L, userName: 'user two', email: ‘test@user.two')
def user3 = new User(id: 3L, userName: 'THIRD test user', email: 'test@user.three')
def user4 = new User(id: 4L, userName: 'testing user four', email: 'test@user.four')
def subject = 'some test email subject'
def content = 'some content of the email'
when:
def result = underTest.sendEmailsToUsers(subject, content, 'test')
then: "get users from repository and send email to each matching user"
1 * userRepository.findAll() >> [user1, user2, user3, user4]
1 * emailSender.sendEmail(subject, content, 'test@user.one')
1 * emailSender.sendEmail(subject, content, 'test@user.four')
result == ['test@user.one', 'test@user.four']
}
@Test
public void testSendingEmailsToMatchingUsers() {
//given:
User user1 = new User();
user1.setId(1L);
user1.setUserName("test user one");
user1.setEmail("test@user.one");
User user2 = new User();
user2.setId(2L);
user2.setUserName("test user two");
user2.setEmail("test@user.two");
User user3 = new User();
user3.setId(3L);
user3.setUserName("THIRD test user");
user3.setEmail("test@user.three");
User user4 = new User();
user4.setId(4L);
user4.setUserName("testing user four");
user4.setEmail("test@user.four");
String subject = "some test email subject";
String content = "some content of the email";
when(userRepository.findAll()).thenReturn(Arrays.asList(user1, user2, user3, user4));
//when:
List<String> result = underTest.sendEmailsToUsers(subject, content, "test");
//then:
Assert.assertEquals(Arrays.asList("test@user.one", "test@user.four"), result);
verify(userRepository).findAll();
}
Mockito vs Spock
http://spockframework.org/spock/docs/1.1/index.html
Spock
Thank you.

Spock the enterprise ready specifiation framework - Ted Vinke

  • 1.
    Spock Spock the enterprise readyspecification framework Ted Vinke, 2018
  • 2.
  • 3.
    • Overview • DataDriven Testing • Interaction Based Testing • Extensions
  • 7.
    class MathSpec extendsSpecification { void "should find maximum of two numbers"() { when: def x = Math.max(1, 2) then: x == 2 } }
  • 8.
    class MathSpec extendsSpecification { void "should find maximum of two numbers"() { when: def x = Math.max(1, 2) then: x == 2 } }
  • 9.
    class MathSpec extendsSpecification { void "should find maximum of two numbers"() { when: def x = Math.max(1, 2) then: x == 2 } }
  • 10.
    class MathSpec extendsSpecification { void "should find maximum of two numbers"() { when: def x = Math.max(1, 2) then: x == 2 } }
  • 11.
  • 12.
    setup: def stack =new Stack() def elem = "push me" Setup Blocks
  • 13.
  • 14.
    when: stack.push(elem) then: !stack.empty stack.size() == 2 stack.peek()== elem Plain boolean conditions Conditions Condition not satisfied: stack.size() == 2 | | | | 1 false [push me]
  • 15.
  • 16.
    when: stack.pop() then: def e =thrown(EmptyStackException) Exception conditions Conditions
  • 17.
    when: stack.pop() then: def e =thrown(EmptyStackException) e.cause == null Exception conditions Conditions
  • 18.
    when: def x =Math.max(1, 2) then: x == 2 Expect Blocks expect: Math.max(1, 2) == 2
  • 19.
    setup: def file =new File("/some/path") file.createNewFile() // ... cleanup: file.delete() Cleanup Blocks
  • 20.
  • 21.
    class MathSpec extendsSpecification { def "maximum of two numbers"() { expect: Math.max(1, 3) == 3 Math.max(7, 4) == 7 Math.max(0, 0) == 0 } } The naive way Data Driven Testing • Code and data are mixed and cannot easily be changed independently
 • Data cannot easily be auto-generated or fetched from external sources
 • In order to exercise the same code multiple times, it either has to be duplicated or extracted into a separate method
 • In case of a failure, it may not be immediately clear which inputs caused the failure
 • Exercising the same code multiple times does not benefit from the same isolation as executing separate methods does …has potential drawbacks:
  • 22.
    class MathSpec extendsSpecification { def "maximum of two numbers"() { expect: Math.max(1, 3) == 3 Math.max(7, 4) == 7 Math.max(0, 0) == 0 } } Refactored Data Driven Testing
  • 23.
    class MathSpec extendsSpecification { def "maximum of two numbers"() { expect: } } Refactored Data Driven Testing
  • 24.
    class MathSpec extendsSpecification { def "maximum of two numbers"(int a, int b, int c) { expect: } } Refactored Data Driven Testing
  • 25.
    class MathSpec extendsSpecification { def "maximum of two numbers"(int a, int b, int c) { expect: Math.max(a, b) == c ... } } Refactored Data Driven Testing
  • 26.
    class MathSpec extendsSpecification { def "maximum of two numbers"(int a, int b, int c) { expect: Math.max(a, b) == c } } Data Tables Data Driven Testing
  • 27.
    class MathSpec extendsSpecification { def "maximum of two numbers"(int a, int b, int c) { expect: Math.max(a, b) == c where: a | b | c 1 | 3 | 3 7 | 4 | 7 0 | 0 | 0 } } Data Tables Data Driven Testing
  • 28.
    class MathSpec extendsSpecification { def "maximum of two numbers"() { expect: Math.max(a, b) == c where: a | b | c 1 | 3 | 3 7 | 4 | 7 0 | 0 | 0 } } Data Tables Data Driven Testing method parameters omitted
  • 29.
    class MathSpec extendsSpecification { def "maximum of two numbers"() { expect: Math.max(a, b) == c where: a | b || c 1 | 3 || 3 7 | 4 || 7 0 | 0 || 0 } } Data Tables Data Driven Testing visually seperate inputs and outputs
  • 30.
    class MathSpec extendsSpecification { def "maximum of two numbers"() { expect: Math.max(a, b) == c where: a | b || c 1 | 3 || 3 7 | 4 || 7 0 | 0 || 0 } } Data Tables with visually seperated inputs and outputs Data Driven Testing
  • 31.
    class MathSpec extendsSpecification { def "maximum of two numbers"() { expect: Math.max(a, b) == c where: a | b || c 1 | 3 || 3 7 | 4 || 7 0 | 0 || 0 } } Reporting of failures Data Driven Testing maximum of two numbers FAILED Condition not satisfied: Math.max(a, b) == c | | | | | | 7 4 | 7 42 false
  • 32.
    @Unroll def "maximum oftwo numbers"() Reporting of failures Data Driven Testing maximum of two numbers[0] PASSED maximum of two numbers[1] FAILED Math.max(a, b) == c | | | | | | 7 4 | 7 42 false maximum of two numbers[2] PASSED
  • 33.
    @Unroll def "maximum of#a and #b is #c"() Reporting of failures Data Driven Testing maximum of 3 and 5 is 5 PASSED maximum of 7 and 4 is 7 FAILED Math.max(a, b) == c | | | | | | 7 4 | 7 42 false maximum of 0 and 0 is 0 PASSED
  • 34.
    ... where: a << [1,7, 0] b << [3, 4, 0] c << [3, 7, 0] Data pipes Data Driven Testing ... where: a = 3 b = Math.random() * 100 c = a > b ? a : b
  • 35.
    @Shared sql =Sql.newInstance("jdbc:h2:mem:", "org.h2.Driver") def "maximum of two numbers"() { expect: Math.max(a, b) == c where: [a, b, c] << sql.rows("select a, b, c from maxdata") } Data pipes Data Driven Testing
  • 36.
  • 37.
    interface Subscriber { voidreceive(String message) } Publishers and subscribers Interaction Based Testing class Publisher { List<Subscriber> subscribers = [] int messageCount = 0 void send(String message){ subscribers*.receive(message) messageCount++ } }
  • 38.
    class PublisherSpec extendsSpecification { Publisher publisher = new Publisher() } Publishers and subscribers Interaction Based Testing class Publisher { List<Subscriber> subscribers = [] int messageCount = 0 void send(String message){ subscribers*.receive(message) messageCount++ } } interface Subscriber { void receive(String message) }
  • 40.
    class PublisherSpec extendsSpecification { Publisher publisher = new Publisher() } } Publishers and subscribers Interaction Based Testing
  • 41.
    class PublisherSpec extendsSpecification { Publisher publisher = new Publisher() Subscriber subscriber = Mock() Subscriber subscriber2 = Mock() } } Creating mocks Interaction Based Testing
  • 42.
    class PublisherSpec extendsSpecification { Publisher publisher = new Publisher() Subscriber subscriber = Mock() Subscriber subscriber2 = Mock() def setup() { // << is a Groovy shorthand for List.add() publisher.subscribers << subscriber publisher.subscribers << subscriber2 }} } Injecting mocks Interaction Based Testing
  • 43.
    def "should sendmessages to all subscribers"() { } Mocking Interaction Based Testing
  • 44.
    def "should sendmessages to all subscribers"() { when: publisher.send("hello") } Mocking Interaction Based Testing
  • 45.
    def "should sendmessages to all subscribers"() { when: publisher.send("hello") then: 1 * subscriber.receive("hello") 1 * subscriber2.receive("hello") } Mocking Interaction Based Testing
  • 46.
    def "should sendmessages to all subscribers"() { when: publisher.send("hello") then: 1 * subscriber.receive("hello") 1 * subscriber2.receive("hello") } Mocking Interaction Based Testing interactions
  • 47.
    1 * subscriber.receive("hello") || | | | | | argument constraint | | method constraint | target constraint cardinality Interactions Interaction Based Testing
  • 48.
    1 * subscriber.receive("hello")// exactly one call 0 * subscriber.receive("hello") // zero calls (1..3) * subscriber.receive("hello") // between one and three calls (1.._) * subscriber.receive("hello") // at least one call (_..3) * subscriber.receive("hello") // at most three calls _ * subscriber.receive("hello") // any number of calls Cardinality Interaction Based Testing
  • 49.
    1 * subscriber.receive("hello")// exactly one call 0 * subscriber.receive("hello") // zero calls (1..3) * subscriber.receive("hello") // between one and three calls (1.._) * subscriber.receive("hello") // at least one call (_..3) * subscriber.receive("hello") // at most three calls _ * subscriber.receive("hello") // any number of calls Cardinality Interaction Based Testing
  • 50.
    1 * subscriber.receive("hello")// a call to 'subscriber' 1 * _.receive("hello") // a call to any mock object Target Constraint Interaction Based Testing
  • 51.
    1 * subscriber.receive("hello")// a method named 'receive' 1 * subscriber./r.*e/("hello") // matches regular expression Method Constraint Interaction Based Testing
  • 52.
    1 * subscriber.receive("hello")// an argument equal to the String "hello" 1 * subscriber.receive(!"hello") // an argument unequal to the String "hello" 1 * subscriber.receive() // the empty argument list 1 * subscriber.receive(_) // any single argument (including null) 1 * subscriber.receive(*_) // any argument list (including empty) 1 * subscriber.receive(!null) // any non-null argument 1 * subscriber.receive(_ as String) // any non-null argument that is-a String 1 * subscriber.receive({ it.size() > 3 }) // an argument that satisfies predicate Argument Constraints Interaction Based Testing
  • 53.
    1 * subscriber.receive("hello")// an argument equal to the String "hello" 1 * subscriber.receive(!"hello") // an argument unequal to the String "hello" 1 * subscriber.receive() // the empty argument list 1 * subscriber.receive(_) // any single argument (including null) 1 * subscriber.receive(*_) // any argument list (including empty) 1 * subscriber.receive(!null) // any non-null argument 1 * subscriber.receive(_ as String) // any non-null argument that is-a String 1 * subscriber.receive({ it.size() > 3 }) // an argument that satisfies predicate Argument Constraints Interaction Based Testing 1 * process.invoke("ls", "-a", _, !null, { ["abcdefghiklmnopqrstuwx1"].contains(it) })
  • 54.
    1 * subscriber._(*_) QuizBased Testing 1 * _
  • 55.
    class MySpec extendsSpecification { Subscriber subscriber = Mock def setup() { 1 * subscriber.receive("hello") 1 * subscriber.receive("goodbye") } def "test"() { } } Interactions everywhere! class MySpec extends Specification { Subscriber subscriber = Mock { 1 * receive("hello") 1 * receive("goodbye") } def "test"() { } } class MySpec extends Specification { Subscriber subscriber = Mock def "test"() { with(subscriber) { 1 * receive("hello") 1 * receive("goodbye") } } }
  • 56.
    when: publisher.send("message1") then: 1 * subscriber.receive("message1") when: publisher.send("message2") then: 1* subscriber.receive("message2") Scope of Interactions Interaction Based Testing
  • 57.
    Verification of Interactions InteractionBased Testing Too many invocations for: 2 * subscriber.receive(_) (3 invocations) Matching invocations (ordered by last occurrence): 2 * subscriber.receive("hello") <-- this triggered the error 1 * subscriber.receive("goodbye")
  • 58.
    Verification of Interactions InteractionBased Testing Too few invocations for: 1 * subscriber.receive("hello") (0 invocations) Unmatched invocations (ordered by similarity): 1 * subscriber.receive("goodbye") 1 * subscriber2.receive("hello")
  • 59.
    Stubbing of interactions InteractionBased Testing interface Subscriber { String receive(String message) } subscriber.receive(_) >> "ok"
  • 60.
    subscriber.receive(_) >> "ok" || | | | | | response generator | | argument constraint | method constraint target constraint Response generator Interaction Based Testing
  • 61.
    subscriber.receive(_) >> "ok" Returningvalues Interaction Based Testing subscriber.receive("message1") >> "ok" subscriber.receive("message2") >> "fail" subscriber.receive(_) >>> ["ok", "error", "error", "ok"] subscriber.receive(_) >> { String message -> message.size() > 3 ? "ok" : "fail" }
  • 62.
    def subscriber =Stub(Subscriber) Other kinds of mock objects Interaction Based Testing Stubs Spies def subscriber = Spy(SubscriberImpl, constructorArgs: ["Fred"])
  • 63.
  • 64.
    @Ignore def "my feature"(){ ... } @IgnoreIf({ System.getProperty("os.name").contains("windows") }) def "my feature"() { ... } @Requires({ os.windows }) class MySpec extends Specification { ... } Ignoring and requiring Extensions
  • 65.
    @PendingFeature def "not implementedyet"() { ... } Pending features Extensions
  • 66.
    @Timeout(5) def "I failif I run for more than five seconds"() { ... } @Timeout(value = 100, unit = TimeUnit.MILLISECONDS) def "I better be quick" { ... } Timeouts Extensions
  • 67.
    @Narrative(""" As a user Iwant foo So that bar """) @See("http://spockframework.org/spec") class MySpec { @Issue("http://my.issues.org/FOO-2") def "Foo should do bar"() { ... } } Documentation Extensions
  • 68.
    def "should sendemails to matching users"() { given: def user1 = new User(id: 1L, userName: 'user one', email: 'test@user.one') def user2 = new User(id: 2L, userName: 'user two', email: ‘test@user.two') def user3 = new User(id: 3L, userName: 'THIRD test user', email: 'test@user.three') def user4 = new User(id: 4L, userName: 'testing user four', email: 'test@user.four') def subject = 'some test email subject' def content = 'some content of the email' when: def result = underTest.sendEmailsToUsers(subject, content, 'test') then: "get users from repository and send email to each matching user" 1 * userRepository.findAll() >> [user1, user2, user3, user4] 1 * emailSender.sendEmail(subject, content, 'test@user.one') 1 * emailSender.sendEmail(subject, content, 'test@user.four') result == ['test@user.one', 'test@user.four'] } @Test public void testSendingEmailsToMatchingUsers() { //given: User user1 = new User(); user1.setId(1L); user1.setUserName("test user one"); user1.setEmail("test@user.one"); User user2 = new User(); user2.setId(2L); user2.setUserName("test user two"); user2.setEmail("test@user.two"); User user3 = new User(); user3.setId(3L); user3.setUserName("THIRD test user"); user3.setEmail("test@user.three"); User user4 = new User(); user4.setId(4L); user4.setUserName("testing user four"); user4.setEmail("test@user.four"); String subject = "some test email subject"; String content = "some content of the email"; when(userRepository.findAll()).thenReturn(Arrays.asList(user1, user2, user3, user4)); //when: List<String> result = underTest.sendEmailsToUsers(subject, content, "test"); //then: Assert.assertEquals(Arrays.asList("test@user.one", "test@user.four"), result); verify(userRepository).findAll(); } Mockito vs Spock
  • 69.
  • 70.