April 11, 2017

Spocklight: Set Timeout On Specification Methods

When we write a feature method in our Spock specification to test our class we might run into long running methods that are invoked. We can specify a maximum time we want to wait for a method. If the time spent by the method is more than the maximum time our feature method must fail. Spock has the @Timeout annotation to define this. We can apply the annotation to our specification or to feature methods in the specification. We specify the timeout value as argument for the @Timeout annotation. Seconds are the default time unit that is used. If we want to specify a different time unit we can use the annotation argument unit and use constants from java.util.concurrent.TimeUnit to set a value.

In the following example specification we set a general timeout of 1 second for the whole specification. For two methods we override this default timeout with their own value and unit:

package mrhaki.spock

@Grab('org.spockframework:spock-core:1.0-groovy-2.4')
import spock.lang.Specification
import spock.lang.Subject
import spock.lang.Timeout

import static java.util.concurrent.TimeUnit.MILLISECONDS

// Set a timeout for all feature methods.
// If a feature method doesn't return in 1 second
// the method fails.
@Timeout(1)
class SampleSpec extends Specification {

    @Subject
    private final Sample sample = new Sample()

    // Check that method will return within 1 second.
    void 'timeout will not happen'() {
        expect:
        sample.run(500) == 'Awake after 500 ms.'
    }

    // Method will fail, because it doesn't return in 1 second.
    void 'method under test should return in 1 second'() {
        expect:
        sample.run(1500) == 'Awake after 1500 ms.'
    }

    // We can change the timeout value and 
    // the unit. The unit type is 
    // java.util.concurrent.TimeUnit.
    @Timeout(value = 200, unit = MILLISECONDS)
    void 'method under test should return in 200 ms'() {
        expect:
        sample.run(100) == 'Awake after 100 ms.'
    }

    // Method will fail.
    @Timeout(value = 100, unit = MILLISECONDS)
    void 'method under test should return in 100 ms'() {
        expect:
        sample.run(200) == 'Awake after 200 ms.'
    }

}

// Simple class for testing.
class Sample {
    /**
     * Run method and sleep for specified timeout value.
     *
     * @param timeout Sleep number of milliseconds specified
     *                by the timeout argument.
     * @return String value with simple message.
     */
    String run(final Long timeout) {
        sleep(timeout)
        "Awake after $timeout ms."
    }
}

Written with Spock 1.0-groovy-2.4.