Monday, May 14, 2012

Spock: Using @Unroll with Parameterized Data

With Spock, a testing framework for Java and Groovy, we can easily write tests using parameterized data. One feature I'd like to share is the @Unroll annotation which makes working with parameterized data easier as each entry in the table will be treated as separate test.

Take this example that tests the addition of two numbers:

    package org.stash.example

    import spock.lang.Specification
    import spock.lang.Unroll

    class AdderSpec extends Specification {

        Adder adder = new Adder()

        @Unroll("#i + #j == #sum?")
        def 'can add two numbers together to produce a sum'() {
            expect:
                sum == adder.add(i, j)
            where:
                i | j | sum
                1 | 2 | 3
                2 | 2 | 4
               -1 | 2 | 0
        }
    }

Prior to adding the @Unroll annotation, our test output in Maven would look like this:

    Running org.stash.example.AdderSpec
    Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.374 sec

The three separate test are treated as a whole. With the annotation added:

    Running org.stash.example.AdderSpec
    Tests run: 3, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.388 sec

Now each entry for the data table is treated as a separate test

If we were to force a failure, by say expecting -1 + 2 == 0...

    Running org.stash.example.AdderSpec
    Tests run: 3, Failures: 1, Errors: 0, Skipped: 0, Time elapsed: 0.501 sec <<< FAILURE!

    Results :

    Failed tests:   -1 + 2 == 0?(org.stash.example.AdderSpec): Condition not satisfied:(..)

We now see in the output the message attached to the annotation with the values used in the test from the data table filtered in!

2 comments:

  1. In JUnit, multiple test cases can be executed in a loop with: @RunWith(Parameterized.class)
    How can this be achieved in Spock?

    I mean if you have multiple def for a single test, how you can loop with different sets of input data?
    Using where block in a def only iterates inside itself.

    ReplyDelete