spock-workshop-lectures



spock-workshop-lectures

0 0


spock-workshop-lectures

http://tbialecki.github.io/spock-workshop-lectures

On Github tbialecki / spock-workshop-lectures

Spock Framework

Tomasz Białecki

Spock is ...

... a developer testing and specification framework

... for Java and Groovy

... compatible with most IDEs, build tools, and continuous integration servers

... is inspired from JUnit, jMock, RSpec, Groovy, Scala, Vulcans

State based testing - Java

public class HelloWorldTest {
    @Test
    public void plusPlusIShouldIterate() {
        //given
        int i = 0;
        //when
        i = ++i;
        //then
        assertEquals(i, 1);
    }
}

State based testing - Spock

class HelloWorldTest extends Specification {

    def "++i should iterate"() {
        given:
            def i = 0
        when:
            i = ++i
        then:
            i == 1
    }
}

State based testing - Spock

class HelloWorldTest extends Specification {

    def "++i should iterate"() {
        given: "i variable"
            def i = 0
        when: "using ++ operator"
            i = ++i
        then: "i should be equal 1"
            i == 1
    }
}

State based testing - Spock

class HelloWorldTest extends Specification {

    def "++i should iterate"() {
        given: "i variable"
            def i = 0
        and: "some programming skills"
            //the programming skills
        when: "using ++ operator"
            i = ++i
        then: "i should be equal 1"
            i == 1
    }
}

State based testing - Spock

class HelloWorldTest extends Specification {

    def "++i should iterate"() {
        given:
            def i = 0
        expect:
            ++i == 1
    }
}

Spec lifecycle

class LifecycleTest extends Specification {

    @Shared
    def objectUnderTest = new Simple();

    def setup() { ... }

    def cleanup() { ... }

    def setupSpec() { ... }

    def cleanupSpec() { ... }
}

Meaningful error messages

class HelloWorldTest extends Specification {

    def "failing tests should give reasonable output message"() {
        when:
            def list = [[1, 2, 3], [2, 3, 4]];
        then:
            list.get(0).sum() == 7

    }
}
Condition not satisfied:

list.get(0).sum() == 7
|    |      |     |
|    |      6     false
|    [1, 2, 3]
[[1, 2, 3], [2, 3, 4]]

...

Data driven tests

class DataDrivenTest extends Specification {

  def "Math.max should determine max value"(int a, int b, int max) {

    expect:
      Math.max(a, b) == max

    where:
      a | b || max
      1 | 2 || 2
      1 | 1 || 1
      3 | 2 || 3
  }
}

Data driven tests

class DataDrivenTest extends Specification {

  def "Math.max should determine max value"() {

    expect:
      Math.max(a, b) == max

    where:
      a | b || max
      1 | 2 || 2
      1 | 1 || 1
      3 | 2 || 3
  }
}

Data driven tests - pipes

class DataDrivenTest extends Specification {

  def "Math.max should determine max value"(int a, int b, int max) {

    expect:
      Math.max(a, b) == max

    where:
      a << [1, 1, 3]
      b << [2, 1, 2]
      max << [2, 1, 3]
  }
}

...

Interaction Based Testing

Own mocking implementation

... tightly integrated with Spock features

... but not obligatory

Interaction Based Testing - Mocking

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

interface Subscriber {
    void receive(String message)
}
def "should notify subscriber"() {
    given:
        def publisher = new Publisher();
        def subscriber = Mock(Subscriber);
        publisher.subscribers << subscriber;

    when:
        publisher.send("hello")

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

Interaction Based Testing

Cardinality constraints

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

Target constraints

1 * subscriber.receive("hello")      // a call to 'subscriber'
1 * _.receive("hello")               // a call to any mock object

Method constraints

1 * subscriber.receive("hello") // a method named 'receive'
1 * subscriber./r.*e/("hello")  // a method whose name matches
                                // the given regular expression

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
1 * subscriber.receive(*_)          // any argument 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
                                    // the given predicate

Multiargument constraints

1 * process.invoke("ls", "-a", _, !null, { ["abcd"].contains(it) })

Interaction Based Testing - Stubbing

interface Subscriber {
    String receive(String message)
}

Returning Fixed Values

subscriber.receive("message1") >> "ok"
subscriber.receive("message2") >> "fail"

Returning Sequences of Values

subscriber.receive(_) >>> ["ok", "error", "error", "ok"]

Computing Return Values

subscriber.receive(_) >> { args -> args[0].size()>3 ? "ok": "nok" }

Chaining Method Responses

subscriber.receive(_) >>> ["ok", "fail", "ok"]
                      >> { throw new InternalError() } >> "ok"

Extensions

@Ignore
def "I won't run"() { ... }

@IgnoreRest
def "Only it will run"() { ... }

@IgnoreIf({ os.windows })
def "I'll run everywhere but on Windows"() { ... }

@Requires({ os.windows })
def "I'll only run on Windows"() { ... }
@Stepwise
class RunInOrderSpec extends Specification { ... }

@Timeout(10)
class TimedSpec extends Specification { ... }

Spring support

@ContextConfiguration(classes = SampleComponentTestConfig)
class SampleComponentTest extends Specification {
    @Autowired
    SampleComponent sampleComponent;

    def "should do it"() {
        when:
            def output = sampleComponent.doIt();
        then:
            output == "I`m doing";
    }

    @Configuration
    public static class SampleComponentTestConfig {
        @Bean
        public SampleComponent sampleComponent() {
            return new SampleComponent();
        }
    }
}
Spock Framework Tomasz Białecki