Search code examples
unit-testingtestingspock

Parameterize Spock setup


Is it possible to parameterize a Spock setup?

By that I mean, imagine I have an object whose state I want to test. The object can have multiple states, but to simplify things, let's say there's one I'm particularly interested in, S1.

There are multiple ways to get the object to S1. I'm testing state, so all the tests for S1 will be the same regardless of how the object reached S1. The one thing that would differ between test cases would be the setup strategy.

One way to deal with this is to have a base test case (or "spec" to use Spock parlance) and subclasses that only supply different setup strategies.

But, given the nice data-driven features of tests that Spock offers, I got to wondering if there might be some way to parameterize the setup in such a way that I wouldn't need concrete subclass specs.

In effect, I would be saying, here's a spec, now run it with these different ways of executing setup.


Solution

  • Always nice to have at least a bit of code as example, but here you go.
    Trying to put your words into a data-driven Spec:

    class MyObjectSpec extends Specification {
    
        @Unroll
        def "Using setup strategy '#desc', instance of MyObject will reach state S1" {
    
            setup:
            def myObject = new MyObject()
            setupStrategy.call(myObject)
    
            expect:
            hasStateS1(myObject)
    
            where:
            setupStrategy           | desc
            { setupThisWay(it)    } | 'this way'
            { setupThatWay(it)    } | 'that way'
            { setupAnotherWay(it) } | 'another way'
        }
    
        void setupThisWay(MyObject myObject) {
            // mutate myObject
        }
    
        // setupThatWay, setupAnotherWay
    
        void hasStateS1(MyObject myObject) {
            assert myObject.hasStateS1()
        }
    }
    
    

    We are using Groovy closures, defined inline in the data table to call helper methods that mutate your newly created object.

    In this case, there is no when block, because you kind of implied that you wanteded the interesting part to happen in setup (which is the same as given), and expect is a then, that doesn't need a when.

    The helper methods are of type void, and do their own assertions, as suggested in the Spock documentation (in fact making the then/expect no-op, however the Spock compiler enforces it anways).

    Data-driven docs are here.