Spock framework - Other features

Spock

In this article we will continue exploring more interesting features that Spock has to offer:

Let's start with the old method:

Considering a class that implements a buffer:

class Buffer {

    StringBuilder builder = new StringBuilder()

    Buffer(String text) {
        builder << text
    }

    void append(String text) {
        builder << text
    }

    String getText() {
        builder.toString()
    }

}

And the following unit test for it:

import spock.lang.Specification

class BufferSpec extends Specification {

    void 'appends text to buffer'() {
        given:
        def word = 'david'
        def buffer = new Buffer('hello')

        when:
        buffer.append(word)

        then:
        buffer.text == old(buffer.text) + word
    }
}

We can see that Spock is able to know what the value of the buffer.text variable was before the action append was called.

Testing exceptions:

You can easily test exceptions using the thrown keyword:

class UserValidatorUnitSpec extends Specification {

    private Validator validator

    void 'user with empty email fails validation'() {
        given:
        def user = new User(name: 'David', email: '')

        when:
        validator.validate(user)

        then:
        thrown ValidationException
    }
}

Or if you want to check more information about the exception:

class UserValidatorUnitSpec extends Specification {

    private Validator validator

    void 'user with empty email fails validation'() {
        given:
        def user = new User(name: 'David', email: '')

        when:
        validator.validate(user)

        then:
        ValidationException exception = thrown()
        exception.message == 'user.email cannot be empty'
    }
}
Complex scanarios with @Stepwise

@Stepwise is an annotation that we can use to execute tests that depend on something that needs to have happened before. In general it is a bad idea not to have isolated tests, but I will present this annotation since it can be useful in certain cases.

@Stepwise
class DeliveryResourceFunctionalSpec extends ApiFunctionalSpec implements UserResource, DeliveryResource {

    private static final USER_RESOURCE = '/user'
    private static final DELIVERY_RESOURCE = '/delivery'

    void 'creates a new user'() {
        when:
        def response = post(resource: USER_RESOURCE, content: aUser())

        then:
        response.statusCode == HTTP_CREATED
    }

    void 'GET /delivery/{id} successfully'() {
        when:
        def response = get(id: 1, resource: DELIVERY_RESOURCE)

        then:
        response.statusCode == HTTP_OK
        assertLenientEquals(json(response), parse(delivery()))
    }
}    

This is an example of a functional test in which I am exercising an API. I am interested in retrieving a delivery, and in the underlying model, the delivery belongs to a user, so a user must exist first before the delivery. You can either use this technique, or just create a user in the system without exercising the API through the first test. Use this technique wisely.

The @IgnoreIf annotation

You can use this annotation to avoid the execution of a test when a specific condition happens. Let's see an example:

class ConfigFileSpec extends Specification {

    @IgnoreIf({ isOsWindows() })
    void 'config file contains the environment config'() {
        expect:
        new File('/tmp/config').text == 'configuration...'
    }

    private static boolean isOsWindows() {
        System.properties['os.name'] == 'windows'
    }

}

You can also exclude tests if the Java version is not the one that you require in the project with @IgnoreIf({ javaVersion < 1.8 }) or ignore tests based on system properties or environment variables with @IgnoreIf({ Boolean.valueOf(properties['env.staging']) }), @IgnoreIf({ Boolean.valueOf(env['ENV_STAGING']) }

Property testing

I am going to finally reference a library called Spock Genesis that you can use to write your property tests. Let's see an example of it:

@Unroll
void 'test reverse #string'() {  
    when:
    def reversed = string.reverse()

    then:
    reversed.size() == string.size()
    string.eachWithIndex { letter, index -> assert letter == reversed[-(index + 1)] }
    reversed.reverse() == string

    where:
    string << (Gen.string).take(10000).findAll()
}

I hope you enjoyed this overview of Spock and you find interesting the advanced capabilities and features that it offers in comparison to other testing libraries.

Happy testing!

For more information about Spock, visit the Spock Framework Reference Documentation.