I use Spock almost exclusively to test Groovy or Java code these days. It’s got some fantastic features that other test frameworks don’t have. Some of them aren’t that well known or documented, though.
old method is possibly my favourite Spock feature. It’s a simple thing but really enhances test legibility. It’s also great for wowing developers new to Spock because it looks like black magic at first glance.
Consider a simple test like this:
when: myList << 'foo' then: myList.size() == 1
This is fine in simple cases, but what happens if
myList.size() is in a less predictable state? I’ve used this technique for testing cache usage for instance; making assertions about hit & miss counts and cache size. Those numbers are less predictable as they’ll be affected by other things (concurrency or side-effects of test setup, for example). Hardcoding expected values makes the tests brittle.
We can improve things a little by comparing the value after the
when: block with the value before it:
given: def oldSize = myList.size() when: myList << 'foo' then: myList.size() == oldSize + 1
old method there’s an even neater way, though:
when: myList << 'foo' then: myList.size() == old(myList.size()) + 1
Wait, WTF is going on there? That’s voodoo!
In fact Spock is using an AST transformation to execute the call to
old before the
when: block. If you step through the spec with the debugger in IntelliJ Idea you’ll see exactly what’s going on. The execution jumps to the line with
old before the
when: block lines, then visits it again afterwards.
old method returns the value an arbitrary statement had before the preceding
when: block was executed. Because of this
old can only be used in a
then: block (or any subsequent
There’s no limit to what you can do with
old. I’ve used it in Geb specs to do things like this:
given: to CocktailListPage when: to NewCocktailPage cocktail.name = 'Dirty Martini' cocktail.save.click() then: at CocktailListPage and: cocktailList.names == old(cocktailList.names) + ['Dirty Martini']