Spock framework - Test doubles

Spock

In our previous article, we covered some of the main features that Spock offers. In this article we will continue exploring the mocking capabilities of the framework.

If we continue with our previous example of the REST API that tries to find an existing user in our system, probably the next step would be to introduce the concept of a repository from which we can retrieve the users that are somehow stored in the system.

If we follow this approach, then we need to modify our test to deal with a repository collaborator, and if we want to test the UserController class in isolation, we need to use a test double that mimics the behavior of the repository. Our test will look now like this:

import spock.lang.Specification  
import spock.lang.Subject

class UserControllerSpec extends Specification {

    @Subject
    private userController
    private userRepository

    void setup() {
        userRepository = Stub(UserRepository)
        userController = new UserController(userRepository)
    }

    void 'calls user repository to find a user'() {
        given: 'an existing user'
        def user = [id: 1, name: 'David']

        when: 'controller show action is called with the user id'
        def response = userController.show(user.id)

        then: 'the repository finds the user'
        userRepository.find(user.id) >> user

        and: 'the response contains the user found by the repository'
        response == user
    }

}

And the Java class to make this test pass:

import java.util.Map;

public class UserController {

    private UserRepository userRepository;

    public UserController(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    public Map show(Integer id) {
        return userRepository.find(id);
    }

}

There are quite a few things going on in this test. First, we are introducing the @Subject annotation that comes with Spock, this is totally optional, but it helps to identify the Subject Under Test, in our case, the UserController. Second, the UserController is now being instantiated with the UserRepository dependency. Next, we create a Stub for the UserRepository since we are treating it as a dependency of the unit that we want to test in isolation, the UserController.

If we take a look at the test implementation, we will find it pretty obvious what it is doing. Given an initial state in which we have an existing user, when the controller's show action is invoked with the existing user id, then in order to get a response back from the controller, the userRepository.find method is called with the user id, and it returns the instance of the existing user.

The way to specify that a Stub returns a value is with the double right-shift symbol >>. Finally, we verify that the response returned by the controller is the same as the user returned by the repository.

Something relevant to highlight here is that we can add comments after the given/when/then/and tags in order to document our tests in a better way if need it. There are also ways to generate documentation reports based on these comments, so the tests can act as a living documentation of our project.

If instead of using a Stub we use a Mock, we can check in a more strict way that our repository dependency has been called, and that it has been called only once. We can do so by changing the test to this:

import spock.lang.Specification  
import spock.lang.Subject

class UserControllerSpec extends Specification {

    @Subject
    private userController
    private userRepository

    void setup() {
        userRepository = Mock(UserRepository)
        userController = new UserController(userRepository)
    }

    void 'calls user repository to find a user'() {
        given: 'an existing user'
        def user = [id: 1, name: 'David']

        when: 'controller show action is called with the user id'
        def response = userController.show(user.id)

        then: 'the repository finds the user'
        1 * userRepository.find(user.id) >> user

        and: 'the response contains the user found by the repository'
        response == user
    }

}

With the expression 1 * userRepository.find(user.id) >> user we check that the userRepository was called exactly once. If you don't know the difference between a Stub and a Mock, I encourage you to read this article from Martin Fowler Mocks Aren't Stubs. Also, something to mention here is that we should be careful when using mocks (and doubles in general) since they make our tests more coupled to the Subject Under Test, and if we don't use them properly we can end up having lots of tests failures if we change the implementation of the SUT.

Another cool feature of Spock is that we can pre-program our test doubles to return a sequence of values by using the triple right-shift symbol >>>. So if we have a Stub like this

userRepository.find(_) >>> ["david", "sarah", "julia", "elena"]

the first call to userRepository.find will return david, the second call will return sarah, and so on. This is called, interaction based testing. The underscore notation in userRepository.find(_) means that we don't really care about what argument we pass to the find method.

Finally, in the next article, we will be covering some more of Spock's interesting features and we will talk about other Spock related libraries.