Spock Argument Constraints vs Argument Capture

I’ve been writing Unit Tests with Spock and Groovy on and off for a couple of years now. I’ve often had to check the properties of object argument parameters that are passed to methods on mocked objects. I’ve used a number of approaches to this, but realised I didn’t fully understand the main differences between them (they all worked fine and did what they were supposed to). So I decided to take a few minutes to mock up a simple test system and try each one in isolation to improve my understanding.

My simple test system consisted of 4 very simple Groovy classes.

User.groovy – User POJO (or rather POGO)

Controller.groovy – this is the class we will test

Service.groovy – service interface we will mock

ControllerTest.groovy – our Spock Unit Test class

Source code of these sample classes is shown at the very end of the article for reference.

Unit Test Overview

These classes are deliberately simple. We call the controller to register a new user – supplying username and email. The controller will create a User object with these values and pass it to the Service to do the actual business logic.

All we are concerned about here is testing the Controller. We can mock in the Service (which is why its only defined as an interface here – we don’t care about the implementation for now). In terms of the unit testing – we just need to ensure that the controller correctly creates the User object and passes it on to the service.

(Note: in a real world scenario we would also build in tests for error scenarios and the like – but that’s not important for the purposes of this article).

Spock Argument Constraints

To test that the user object the controller passes to the service is what we expect – we can use ‘argument constraints’. These can be very simple constraints – examples shown below:

1 * mockService.register(_) >> 'id1' // checks that we call the register method on the service called exactly once (strict mocking) and passed any parameter
1 * mockService.register(_ as User) >> 'id1' // verifies that the parameter is of the User class
1 * mockService.register(!null) >> 'id1' // verifies that the parameter is not null

However, we really want to be more precise – and verify the actual properties of the User object passed to the Service match what we originally passed into the Controller. In this instance we can use:

Spock Argument Capture

Similar to argument constraints is argument capture. The syntax for this is slightly different. In the past I’ve used a combination of both to verify argument parameters match what I expect. However, after digging into it a bit more – if that’s all you want you want to do – then I think argument constraints (as outlined above) are a better and cleaner solution than the solution below.

Argument capture is still valid – and gives you more power (if you need it). With the argument capture approach you have programmatic access to the parameter values – and can change them and use them in other ways if you need to (e.g. setting other variables, use to call other methods). A couple of examples are included in the Unit Test below.