Spock — the testing and specification framework for Java and Groovy applications — does an excellent job of avoiding a lot of the boilerplate test code that one typically finds in conventional JUnit tests. It combines the utility of all your beloved Java libraries, along with the syntactic sugar and expressiveness of Groovy into a straightforward DSL (Domain-Specific Language) with first class object mocking and Spring support.
There are already numerous articles detailing the merits of using Spock for your automated tests of Java code, but in this post we will highlight a handy and often overlooked feature of Spock: Data-driven testing.
At IAS, we’ve been working hard to develop our new, first-to-market Consumer Exposure capability. Consumer Exposure maps the consumer journey, showing advertisers the total brand exposure across digital touchpoints. Armed with this insight, advertisers can better understand what influences people in order to turn attention into action. As part of the engineering process naturally we need to incorporate automated tests for various pieces of the solution. But how should we automate the tests of the report scheduler?
Data-driven testing with Spock
We had the great fortune of a software engineering director and a product manager that took the time to meticulously specify report scheduling behavior into a tidy spreadsheet:
But wouldn’t it be awesome if we could take this spreadsheet almost verbatim and turn it into executable automated tests? Spock makes that possible. Spock allows you to define a table of variables and values from which you can make test assertions.
Take a look:
One of the first things you might notice is that Spock allows you to name each test with an arbitrary String that can be as descriptive and readable as you want as well as include punctuation. Contrast this with JUnit where you are restricted by Java’s method naming requirements often resulting in developers kludging together a method name in camelcase such as:
@Test public void givenIsVisibleAndEnabledWhenClickThenListenerIsNotified()
One interesting thing within the initial setup of this test is that the ReportTemplate Java class really only has a default constructor. A constructor that accepts the parameters shown doesn’t actually exist. This is a useful feature of Groovy where you can use a constructor with named parameters and it’ll use the default constructor then call all the various property setters for you. That’s just one typical example of how both Groovy and Spock reduce the amount of test code that must be written.
Test steps are separated into stimulus (when) and response (then) sections. The and keyword just acts as a continuation of the current section and is really only for making the test more readable. Another difference from JUnit you may observe is how there is a lack of explicit assert statements. JUnit tests are sprinkled with assertTrue, assertFalse, assertEquals, assertNotEquals, assertNull, and so on. Spock avoids the need for an assertion API and uses plain boolean expressions or otherwise relies on Groovy Truth. Don’t worry, Spock still provides descriptive feedback concerning any violated test conditions.
The where keyword is specifically for data-driven tests where you will execute multiple tests that are functionally the same but only differ in some input or output values. Each test run will go through the data table row by row, assign the listed values to each of the named variables, and then evaluate the when/then sections. Just like the spreadsheet represents, in this table templateStart, templateEnd, frequency, and days are the inputs while reportStart, reportEnd, and reportRunAfter are the expected outputs derived from the description within the spreadsheet’s “Expected Result” cell. If a Human didn’t need to decipher this description then the spreadsheet would have been even more test-friendly to begin with.
Results
Does this test even work?
Green means good. The test passes. But how can we be confident that this test is really evaluating the data table and conditions correctly? If we tamper with one of the expected dates and re-run the test:
Here you can see Spock providing descriptive feedback about the failure. The report’s computed startDate was April 17th but the tampered data table expected April 19th. As is often said “never trust a test that hasn’t failed”
Spock provides an elegant way to write data-driven tests that are easy on the eyes and comprehensible to causal reviewers. This technique represents a clear and direct way for a developer to affirm conformance to product specifications. Consider using Spock’s data-driven testing feature whenever you’re given any specifications in a tabular format.