spock-talk



spock-talk

0 0


spock-talk

Demo Code for Intro Spock Presentation

On Github thecodesmith / spock-talk

Testing: Supercharged

with Groovy and Spock

About Me

Brian Stewart

About the Talk

Testing with Groovy and Spock

  • Concepts
    • Why Use Groovy and Spock for Your Next Project
    • Quick Intro to the Power of Groovy
    • The Spock Framework in Action
  • Demo

Software Testing

Code Should Be:
  • Written for Humans
  • Clean
  • Robust

Software Testing

Master programmers think of systems as stories to be told rather than programs to be written.

-- Robert C. Martin: Clean Code

The Solution: Spock

  • Expressive Language and Syntax
    • Built on Groovy
    • Highly Readable and Intuitive
    • Detailed Output with Groovy PowerAssert
    • Tests Have Real Documentation Value

The Solution: Spock

  • Concise Code and Reduced Clutter
    • Better Assert Syntax
    • Fewer Annotations

The Solution: Spock

  • Flexibility and Versatility
    • Data-Driven Testing
    • Interaction-Driven Testing
    • Built-in Mocking/Stubbing
    • Seamless Integration with Java or Groovy

Groovy in a Nutshell

  • Runs on the JVM
  • Quick Learning Curve for Java Devs
  • Dynamically Typed
  • Enterprise-ready
    • Spring Framework Integration
    • Performant and Stable
    • Powerful Collections/JSON/XML Support
  • Boilerplate? Gone. Have fun coding.

Groovy Demo

The Spock Framework

The Spock Framework

Introduction to Spock

  • Beautiful and highly expressive specification language
  • Built on JUnit: compatible with most IDEs, build tools, and CI servers

The Spock Framework

Getting Started

Getting Started

Extend Specification Class

import spock.lang.Specification

class SimpleSpec extends Specification {

    def 'maximum of two numbers'() {
        expect:
        Math.max(1, 3) == 4
    }
}

Getting Started

Groovy PowerAssert Output

SimpleSpec
 - maximum of two numbers   FAILED

   Condition not satisfied:

   Math.max(1, 3) == 4
        |         |
        3         false

   at SimpleSpec.maximum of two numbers(Script1.groovy:6)

Logical Code Blocks

given: / when: / then:

class SimpleSpec extends Specification {

    def 'maximum value of list'() {
        given:
        def values = [4, 7, 9, 5]

        when:
        def max = values.max()

        then:
        max == 9
    }
}

Logical Code Blocks

setup: / expect: / where:

import groovy.sql.Sql

class SimpleSpec extends Specification {

    def 'sorted crew names'() {
        setup:
        def starship = new Expando(name: 'Enterprise', captain: name)

        expect:
        starship.captain == name

        where:
        name << ['Kirk', 'Picard']
    }
}

State-Based Testing

State-Based Testing

setup() and setupSpec()

class StateSpec extends Specification {

    Car car

    def setupSpec() {
        car = new Car(make: 'Toyota', model: 'Corolla')
    }

    def setup() {
        car.mileage = 20000
    }
}

State-Based Testing

old() Method

def 'add item to collection'() {
    given: 'a list of values'
    def values = [4, 8, 15, 16]

    when: 'adding a single value'
    values << 23

    then: 'list size increases by 1'
    values.size() == old(values.size()) + 1
}

Data-Driven Testing

Data-Driven Testing

Data Tables

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-Driven Testing

Data Pipes

def 'maximum of two numbers'() {
    ...

    where:
    name << ['kirk', 'spock', 'scotty']
}

Data-Driven Testing

Method Unrolling

@Unroll
def 'maximum of two numbers'() {
    expect:
    Math.max(a, b) == c

    where:
    a | b || c
    1 | 3 || 3
    7 | 3 || 3
    0 | 0 || 0
}

Test iterations reported separately:

maximum of two numbers[0]   PASSED
maximum of two numbers[1]   FAILED

Math.max(a, b) == c
    |    |  |  |  |
    |    7  3  |  3
    7          false

maximum of two numbers[2]   PASSED

Interaction-Based Testing

Interaction-Based Testing

Mocks and Stubs

class Publisher {
    List<Subscriber> subscribers
    void send(String message)
}

interface Subscriber {
    void receive(String message)
}

class PublisherSpec extends Specification {
    Publisher publisher = new Publisher()
}

Interaction-Based Testing

Mocks and Stubs

class PublisherSpec extends Specification {
    Publisher publisher = new Publisher()
    Subscriber subscriber = Mock()
    Subscriber subscriber2 = Mock()

    def setup() {
        publisher.subscribers << subscriber // << is a Groovy shorthand for List.add()
        publisher.subscribers << subscriber2
    }
}

Interaction-Based Testing

Mocks and Stubs

def "should send messages to all subscribers"() {
    when:
    publisher.send("hello")

    then:
    1 * subscriber.receive("hello")
    1 * subscriber2.receive("hello")
}

Interaction-Based Testing

Mocks and Stubs

Declaring Cardinality

1 * subscriber.receive("hello")      // exactly one call
0 * subscriber.receive("hello")      // zero calls
(1..3) * subscriber.receive("hello") // between one and three calls (inclusive)
(1.._) * subscriber.receive("hello") // at least one call
(_..3) * subscriber.receive("hello") // at most three calls
_ * subscriber.receive("hello")      // any number of calls, including zero

Interaction-Based Testing

Mocks and Stubs

Argument Constraints

1 * subscriber.receive("hello")     // an argument that is equal to the String "hello"
1 * subscriber.receive(!"hello")    // an argument that is 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 list)
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 given predicate
                                          // (here: message length is greater than 3)

Spock Demo

Conclusion

The Spock Framework

  • Write expressive, expressive, and robust tests
  • Integration is seamless with Java projects
  • Have fun writing tests!

Thank You

Q & A