Search

Dark theme | Light theme
Showing posts with label Spock. Show all posts
Showing posts with label Spock. Show all posts

April 10, 2023

Spocklight: Testing Asynchronous Code With PollingConditions

In a previous blog post we learned how to use DataVariable and DataVariables to test asynchronous code. Spock also provides PollingConditions as a way to test asynchronous code. The PollingConditions class has the methods eventually and within that accept a closure where we can write our assertions on the results of the asynchronous code execution. Spock will try to evaluate conditions in the closure until they are true. By default the eventually method will retry for 1 second with a delay of 0.1 second between each retry. We can change this by setting the properties timeout, delay, initialDelay and factor of the PollingConditions class. For example to define the maximum retry period of 5 seconds and change the delay between retries to 0.5 seconds we create the following instance: new PollingConditions(timeout: 5, initialDelay: 0.5).
Instead of changing the PollingConditions properties for extending the timeout we can also use the method within and specify the timeout in seconds as the first argument. If the conditions can be evaluated correctly before the timeout has expired then the feature method of our specification will also finish earlier. The timeout is only the maximum time we want our feature method to run.

In the following example Java class we have the methods findTemperature and findTemperatures that will try to get the temperature for a given city on a new thread. The method getTemperature will return the result. The result can be null as long as the call to the WeatherService is not yet finished.

package mrhaki;

import java.util.Arrays;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;

class AsyncWeather {

    private final ExecutorService executorService;
    private final WeatherService weatherService;

    private final Map<String, Integer> results = new ConcurrentHashMap<>();

    AsyncWeather(ExecutorService executorService, WeatherService weatherService) {
        this.executorService = executorService;
        this.weatherService = weatherService;
    }

    // Invoke the WeatherService in a new thread and store result in results.
    void findTemperature(String city) {
        executorService.submit(() -> results.put(city, weatherService.getTemperature(city)));
    }

    // Invoke the WeatherService in a new thread for each city and store result in results.
    void findTemperatures(String... cities) {
        Arrays.stream(cities)
              .parallel()
              .forEach(this::findTemperature);
    }

    // Get the temperature. Value can be null when the WeatherService call is not finished yet.
    int getTemperature(String city) {
        return results.get(city);
    }

    interface WeatherService {
        int getTemperature(String city);
    }
}

To test the class we write the following specification using PollingConditions:

package mrhaki

import spock.lang.Specification
import spock.lang.Subject
import spock.util.concurrent.PollingConditions

import java.util.concurrent.Executors

class AsyncPollingSpec extends Specification {

    // Provide a stub for the WeatherService interface.
    // Return 21 when city is Tilburg and 18 for other cities.
    private AsyncWeather.WeatherService weatherService = Stub() {
        getTemperature(_ as String) >> { args ->
            if ("Tilburg" == args[0]) 21 else 18
        }
    }

    // We want to test the class AsyncWeather
    @Subject
    private AsyncWeather async = new AsyncWeather(Executors.newFixedThreadPool(2), weatherService)

    void "findTemperature and getTemperature should return expected temperature"() {
        when:
        // We invoke the async method.
        async.findTemperature("Tilburg")

        then:
        // Now we wait until the results are set.
        // By default we wait for at  most 1 second,
        // but we can configure some extra properties like
        // timeout, delay, initial delay and factor to increase delays.
        // E.g. new PollingConditions(timeout: 5, initialDelay: 0.5, delay: 0.5)
        new PollingConditions().eventually {
            // Although we are in a then block, we must
            // use the assert keyword in our eventually
            // Closure code.
            assert async.getTemperature("Tilburg") == 21
        }
    }

    void "findTemperatures and getTemperature shoud return expected temperatures"() {
        when:
        // We invoke the async method.
        async.findTemperatures("Tilburg", "Amsterdam")

        then:
        // Instead of using eventually we can use within
        // with a given timeout we want the conditions to
        // be available for assertions.
        new PollingConditions().within(3) {
            // Although we are in a then block, we must
            // use the assert keyword in our within
            // Closure code.
            assert async.getTemperature("Amsterdam") == 18
            assert async.getTemperature("Tilburg") == 21
        }
    }
}

Written with Spock 2.4-groovy-4.0.

Spocklight: Testing Asynchronous Code With DataVariable(s)

Testing asynchronous code needs some special treatment. With synchronous code we get results from invoking method directly and in our tests or specifications we can easily assert the value. But when we don’t know when the results will be available after calling a method we need to wait for the results. So in our specification we actually block until the results from asynchronous code are available. One of the options Spock provides us to block our testing code and wait for the code to be finished is using the classes DataVariable and DataVariables. When we create a variable of type DataVariable we can set and get one value result. The get method will block until the value is available and we can write assertions on the value as we now know it is available. The set method is used to assign a value to the BlockingVariable, for example we can do this in a callback when the asynchronous method support a callback parameter.

The BlockingVariable can only hold one value, with the other class BlockingVariables we can store multiple values. The class acts like a Map where we create a key with a value for storing the results from asynchronous calls. Each call to get the value for a given key will block until the result is available and ready to assert.

The following example code is a Java class with two methods, findTemperature and findTemperatures, that make asynchronous calls. The implementation of the methods use a so-called callback parameter that is used to set the results from invoking a service to get the temperature for a city:

package mrhaki;

import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.function.Consumer;

class Async {

    private final ExecutorService executorService;
    private final WeatherService weatherService;

    Async(ExecutorService executorService, WeatherService weatherService) {
        this.executorService = executorService;
        this.weatherService = weatherService;
    }

    // Find temperature for a city using WeatherService and
    // assign result using callback argument.
    void findTemperature(String city, Consumer<Result> callback) {
        // Here we do an asynchronous call using ExecutorService and
        // a Runnable lambda.
        // We're assigning the result of calling WeatherService using
        // the callback argument.
        executorService.submit(() -> {
            int temperature = weatherService.getTemperature(city);
            var result = new Result(city, temperature);
            callback.accept(result);
        });
    }

    // Find temperature for multiple cities using WeatherService and
    // assign result using callback argument.
    void findTemperatures(List<String> cities, Consumer<Result> callback) {
        cities.forEach(city -> findTemperature(city, callback));
    }

    record Result(String city, int temperature) {
    }

    interface WeatherService {
        int getTemperature(String city);
    }
}

To test our Java class we write the following specification where we use both DataVariable and DataVariables to wait for the asynchronous methods to be finished and we can assert on the resulting values:

package mrhaki

import spock.lang.Specification
import spock.lang.Subject
import spock.util.concurrent.BlockingVariable
import spock.util.concurrent.BlockingVariables

import java.util.concurrent.Executors

class AsyncSpec extends Specification {

    // Provide a stub for the WeatherService interface.
    // Return 21 on the first call and 18 on subsequent calls.
    private Async.WeatherService weatherService = Stub() {
        getTemperature(_ as String) >>> [21, 18]
    }

    // We want to test the class Async
    @Subject
    private Async async = new Async(Executors.newFixedThreadPool(2), weatherService)

    void "findTemperature should return expected temperature"() {
        given:
        // We define a BlockingVariable to store the result in the callback,
        // so we can wait for the value in the then: block and
        // asssert the value when it becomes available.
        def result = new BlockingVariable<Async.Result>()

        when:
        // We invoke the async method and in the callback use
        // our BlockingVariable to set the result.
        async.findTemperature("Tilburg") { Async.Result temp ->
            // Set the result to the BlockingVariable.
            result.set(temp)
        }

        then:
        // Now we wait until the result is available with the
        // blocking call get().
        // Default waiting time is 1 second. We can change that
        // by providing the number of seconds as argument
        // to the BlockingVariable constructor.
        // E.g. new BlockingVariable<Long>(3) to wait for 3 seconds.
        result.get() == new Async.Result("Tilburg", 21)
    }

    void "findTemperatures should return expected temperatures"() {
        given:
        // With type BlockingVariables we can wait for multiple values.
        // Each value must be assigned to a unique key.
        def result = new BlockingVariables(5)

        when:
        async.findTemperatures(["Tilburg", "Amsterdam"]) { Async.Result temp ->
            // Set the result with a key to the BlockingVariables result variable.
            // We can story multiple results in one BlockingVariables.
            result[temp.city()] = temp.temperature()
        }

        then:
        // We wait for the results key by key.
        // We cannot rely that the result are available in the
        // same order as the passed input arguments Tilburg and Amsterdam
        // as the call will be asynchronous.
        // But using BlockingVariables we dont' have to care,
        // we simply request the value for a key and the code will
        // block until it is available.
        result["Amsterdam"] == 18
        result["Tilburg"] == 21
    }
}

Written with Spock 2.3-groovy-4.0.

March 30, 2023

Spocklight: Creating Temporary Files And Directories With FileSystemFixture

If we write specification where we need to use files and directories we can use the @TempDir annotation on a File or Path instance variable. By using this annotation we make sure the file is created in the directory defined by the Java system property java.io.tmpdir. We could overwrite the temporary root directory using Spock configuration if we want, but the default should be okay for most situations. The @TempDir annotation can actually be used on any class that has a constructor with a File or Path argument. Since Spock 2.2 we can use the FileSystemFixture class provided by Spock. With this class we have a nice DSL to create directory structures and files in a simple matter. We can use the Groovy extensions to File and Path to also immediately create contents for the files. If we want to use the extensions to Path we must make sure we include org.apache.groovy:groovy-nio as dependency to our test runtime classpath. The FileSystemFixture class also has the method copyFromClasspath that we can use to copy files and their content directory into our newly created directory structure.

In the following example specification we use FileSystemFixture to define a new directory structure in a temporary directory, but also in our project directory:

package mrhaki

import spock.lang.Specification
import spock.lang.Subject
import spock.lang.TempDir
import spock.util.io.FileSystemFixture

import java.nio.file.Path
import java.nio.file.Paths

class FileFixturesSpec extends Specification {

    /**
     * Class we want to test. The class has a method
     * File renderDoc(File input, File outputDir) that takes
     * an input file and stores a rendered file in the given
     * output directory.
     */
    @Subject
    private DocumentBuilder documentBuilder = new DocumentBuilder()

    /**
     * With the TempDir annotation we make sure our directories and
     * files created with FileSystemFixture are deleted after
     * each feature method run.
     */
    @TempDir
    private FileSystemFixture fileSystemFixture

    void "convert document"() {
        given:
        // Create a new directory structure in the temporary directory
        // <root>
        //  +-- src
        //  |    +-- docs
        //  |         +-- input.adoc
        //  |         +-- convert.adoc
        //  +-- output
        fileSystemFixture.create {
            dir("src") {
                dir("docs") {
                    // file(String) returns a Path and with
                    // groovy-nio module on the classpath we can use
                    // extensions to add text to file. E.g. via the text property.
                    file("input.adoc").text = '''\
                    = Sample

                    Welcome to *AsciidoctorJ*.
                    '''.stripIndent()

                    // Copy file from classpath (src/test/resources)
                    // and rename it at the same time.
                    // Without rename it would be
                    // copyFromClasspath("/samples/sample.adoc")
                    copyFromClasspath("/samples/sample.adoc", "convert.adoc")
                }
            }
            dir("output")
        }

        and:
        // Using resolve we get the actual Path to the file
        Path inputDoc = fileSystemFixture.resolve("src/docs/input.adoc")
        Path convertDoc = fileSystemFixture.resolve("src/docs/convert.adoc")

        // We can also use Paths to resolve the actual Path.
        Path outputDir = fileSystemFixture.resolve(Paths.get("output"))

        when:
        File resultDoc = documentBuilder.renderDoc(inputDoc.toFile(), outputDir.toFile())

        then:
        resultDoc.text =~ "<p>Welcome to <strong>AsciidoctorJ</strong>.</p>"

        when:
        File resultConvert = documentBuilder.renderDoc(convertDoc.toFile(), outputDir.toFile())

        then:
        resultConvert.text =~ "<p>Testing <strong>AsciidoctorJ</strong> with Spock 🚀</p>"
    }

    void "convert document from non-temporary dir"() {
        given:
        // Create FileSystemFixture in our project build directory.
        FileSystemFixture fileSystemFixture = new FileSystemFixture(Paths.get("build"))
        fileSystemFixture.create {
            dir("test-docs") {
                dir("src") {
                    dir("docs") {
                        copyFromClasspath("/samples/sample.adoc")
                    }
                }
                dir("output")
            }
        }

        and:
        Path convertDoc = fileSystemFixture.resolve("test-docs/src/docs/sample.adoc")
        Path outputDir = fileSystemFixture.resolve(Paths.get("test-docs/output"))

        when:
        File resultDoc = documentBuilder.renderDoc(convertDoc.toFile(), outputDir.toFile())

        then:
        resultDoc.text =~ "<p>Testing <strong>AsciidoctorJ</strong> with Spock 🚀</p>"

        cleanup:
        // We can delete the test-docs directory ourselves.
        fileSystemFixture.resolve("test-docs").deleteDir()
    }
}

In order to use the Groovy extensions for java.nio.Path we must add the groovy-nio module to the test classpath. For example we can do this if we use Gradle by using the JVM TestSuite plugin extension:

plugins {
    java
    groovy
}

...

repositories {
    mavenCentral()
}

testing {
    suites {
        val test by getting(JvmTestSuite::class) {
            // Define we want to use Spock.
            useSpock("2.3-groovy-4.0")
            dependencies {
                // Add groovy-nio module.
                implementation("org.apache.groovy:groovy-nio")
            }
        }
    }
}

Written with Spock 2.3-groovy-4.0 and Gradle 8.0.2.

March 24, 2023

Spocklight: Assert Elements In Collections In Any Order

Since Spock 2.1 we have 2 new operators we can use for assertions to check collections: =~ and ==~. We can use these operators with implementations of the Iterable interface when we want to check that a given collection has the same elements as an expected collection and we don’t care about the order of the elements. Without the new operators we would have to cast our collections to a Set first and than use the == operator.

The difference between the operators =~ and ==~ is that =~ is lenient and ==~ is strict. With the lenient match operator we expect that each element in our expected collection appears at least once in the collection we want to assert. The strict match operator expects that each element in our expected collection appears exactly once in the collection we want to assert.

In the following example we see different uses of the new operators and some other idiomatic Groovy ways to check elements in a collection in any order:

package mrhaki

import spock.lang.Specification;

class CollectionConditions extends Specification {

    void "check items in list are present in the same order"() {
        when:
        List<Integer> result = [1, 10, 2, 20]

        then:
        // List result is ordered so the items
        // are not the same as the expected List.
        result != [20, 10, 2, 1]
        result == [1, 10, 2, 20]

        // We can cast the result and expected list to Set
        // and now the contents and order is the same.
        result as Set == [20, 10, 2, 1] as Set
    }

    void "check all items in list are present in any order"() {
        when:
        List<Integer> result = [1, 10, 2, 20]

        then:
        result ==~ [20, 10, 2, 1]

        /* The following assert would fail:
           result ==~ [20, 10]

           Condition not satisfied:

           result ==~ [20, 10]
           |      |
           |      false
           [1, 10, 2, 20]

           Expected: iterable with items [<20>, <10>] in any order
                but: not matched: <1>*/

        // Negating also works
        result !==~ [20, 10]
    }

    void "lenient check all items in list are present in any order"() {
        when:
        List<Integer> result = [1, 1, 10, 2, 2, 20]

        then:
        // result has extra values 1, 2 but with lenient
        // check the assert is still true.
        result =~ [20, 10, 2, 1]

        /* The following assert would fail:

        result =~ [20, 10]

        Condition not satisfied:

        result =~ [20, 10]
        |      |
        |      false
        |      2 differences (50% similarity, 0 missing, 2 extra)
        |      missing: []
        |      extra: [1, 2]
        [1, 10, 2, 20] */

        // Negating works
        result !=~ [20, 10]
    }

    void "check at least one item in list is part of expected list in any order"() {
        when:
        List<Integer> result = [1, 10, 2, 20]

        then:
        result.any { i -> i in [20, 10]}
    }

    void "check every item in list is part of expected list in any order"() {
        when:
        List<Integer> result = [1, 1, 10, 2, 2, 20]

        then:
        result.every { i -> i in [20, 10, 2, 1]}
    }
}

Written with Spock 2.3-groovy-4.0.

October 26, 2021

Spocklight: Adjusting Time With MutableClock

Testing classes that work with date calculations based on the current date and time (now) can be difficult. First of all we must make sure our class under test accepts a java.time.Clock instance. This allows us to provide a specific Clock instance in our tests where we can define for example a fixed value, so our tests don't break when the actual date and time changes. But this can still not be enough for classes that will behave different based on the value returned for now. The Clock instances in Java are immutable, so it is not possible to change the date or time for a Clock instance.

In Spock 2.0 we can use the new MutableClock class in our specifications to have a Clock that can be used to go forward or backward in time on the same Clock instance. We can create a MutableClock and pass it to the class under test. We can test the class with the initial date and time of the Clock object, then change the date and time for the clock and test the class again without having to create a new instance of the class under test. This is handy in situations like a queue implementation, where a message delivery date could be used to see if messages need to be delivered or not. By changing the date and time of the clock that is passed to the queue implementation we can write specifications that can check the functionality of the queue instance.

The MutableClock class has some useful methods to change the time. We can for example use the instant property to assign a new Instant. Or we can add or subtract a duration from the initial date and time using the + and - operators. We can specify the temporal amount that must be applied when we use the ++ or -- operators. Finally, we can use the adjust method with a single argument closure to use a TemporalAdjuster to change the date or time. This last method is useful if we want to specify a date adjustment that is using months and years.

In the following example Java code we have the class WesternUnion that accepts letters with a delivery date and message. The letter is stored and when the deliver method is we remove the letter from our class if the delivery date is after the current date and time.

package mrhaki;

import java.time.Clock;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;

/**
 * Class to mimic a letter delivery service, 
 * that should deliver letters after the letter
 * delivery data has passed.
 */
public class WesternUnion {
    private final Clock clock;
    private final List<Letter> letters = new ArrayList<>();

    /**
     * Default constructor that uses a default Clock with UTC timezone.
     */
    public WesternUnion() {
        this(Clock.systemUTC());
    }

    /**
     * Constructor that accepts a clock, very useful for testing.
     * 
     * @param clock Clock to be used in this class for date calculations.
     */
    WesternUnion(final Clock clock) {
        this.clock = clock;
    }

    /**
     * Store accepted letter.
     * 
     * @param deliveryDate Date the letter should be deliverd.
     * @param message Message for the letter.
     */
    public int acceptLetter(Instant deliveryDate, String message) {
        final Letter letter = new Letter(deliveryDate, message);
        letters.add(letter);
        return letters.size();
    }

    /**
     * "Deliver" letters where the delivery date has passed.
     */
    public int deliver() {
        Instant now = Instant.now(clock);
        letters.removeIf(letter -> letter.getDeliveryDate().isBefore(now));
        return letters.size();
    }

    /**
     * Simple record for a "letter" which a delivery date and message.
     */
    private record Letter(Instant deliveryDate, String message) {
        private Instant getDeliveryDate() {
            return deliveryDate;
        }
    }
}

In order to test this class we want pass a Clock instance where we can mutate the clock, so we can test if the deliver method works if we invoke it multiple times. In the next specification we test this with different usages of MutableClock. Each specification method uses the MutableClock in a different way to test the WesternUnion class:

package mrhaki

import spock.lang.Specification
import spock.lang.Subject
import spock.util.time.MutableClock

import java.time.Duration
import java.time.Instant
import java.time.Period
import java.time.ZoneId
import java.time.ZonedDateTime

class WesternUnionSpec extends Specification {

    // Default time zone used for letter dates.
    private static final ZoneId TZ_HILL_VALLEY = ZoneId.of("America/Los_Angeles")
    
    // Constants used for letter to be send.
    private static final Instant DELIVERY_DATE = 
        ZonedDateTime
            .of(1955, 11, 12, 0, 0, 0, 0, TZ_HILL_VALLEY)
            .toInstant()
    private static final String MESSAGE = 
      'Dear Marty, ... -Your friend in time, "Doc" Emmett L. Brown'

    @Subject
    private WesternUnion postOffice

    void "deliver message after delivery date"() {
        given: "Set mutable clock instant to Sept. 1 1885"
        def clock = new MutableClock(
          ZonedDateTime.of(1885, 9, 1, 0, 0, 0, 0, TZ_HILL_VALLEY))

        and:
        postOffice = new WesternUnion(clock)

        expect:
        postOffice.acceptLetter(DELIVERY_DATE, MESSAGE) == 1

        when:
        int numberOfLetters = postOffice.deliver()

        then: "Delivery date has not passed so 1 letter"
        numberOfLetters  == 1

        when: "Move to delivery date of letter + 1 day and try to deliver"
        // We can change the clock's Instant property directly to 
        // change the date.
        clock.instant = 
            ZonedDateTime
                .of(1955, 11, 13, 0, 0, 0, 0, TZ_HILL_VALLEY)
                .toInstant()
        int newNumberOfLetters = postOffice.deliver()

        then: "Delivery date has passed now so 0 letters left"
        newNumberOfLetters == 0
    }
    
    void "deliver message after adjusting MutableClock using adjust"() {
        given: "Set mutable clock instant to Sept. 1 1885"
        def clock = new MutableClock(
            ZonedDateTime.of(1885, 9, 1, 0, 0, 0, 0, TZ_HILL_VALLEY))
        
        and:
        postOffice = new WesternUnion(clock)

        expect:
        postOffice.acceptLetter(DELIVERY_DATE, MESSAGE) == 1

        when:
        int numberOfLetters = postOffice.deliver()
        
        then: "Delivery date has not passed so 1 letter"
        numberOfLetters  == 1

        when: "Move clock forward 70 years, 2 months and 12 days and try to deliver"
        // To move the clock forward or backward by month or years we need
        // the adjust method. The plus/minus/next/previous methods are applied
        // to the Instant property of the MutableClock and with Instant
        // we can not use months or years.
        clock.adjust {t -> t + (Period.of(70, 2, 12)) }
        int newNumberOfLetters = postOffice.deliver()
        
        then: "Delivery date has passed now so 0 letters left"
        newNumberOfLetters == 0
    }

    void "deliver message after adding amount to MutableClock"() {
        given: "Set mutable clock instant to Oct. 26 1955"
        def clock = new MutableClock(
            ZonedDateTime.of(1955, 10, 26, 0, 0, 0, 0, TZ_HILL_VALLEY))

        and:
        postOffice = new WesternUnion(clock)

        expect:
        postOffice.acceptLetter(DELIVERY_DATE, MESSAGE) == 1

        when:
        int numberOfLetters = postOffice.deliver()

        then: "Delivery date has not passed so 1 letter"
        numberOfLetters  == 1

        and: "Move clock forward by given amount (18 days) and try to deliver"
        // The +/- operators are mapped to plus/minus methods. 
        // Amount cannot be months or years, then we need the adjust method.
        clock + Duration.ofDays(18)

        when: "Try to deliver now"
        int newNumberOfLetters = postOffice.deliver()

        then: "Delivery date has passed now so 0 letters left"
        newNumberOfLetters == 0
    }
    
    void "deliver message with fixed change amount"() {
        given: "Set mutable clock instant to Oct. 26 1955"
        def defaultTime = 
            ZonedDateTime.of(1955, 10, 26, 0, 0, 0, 0, TZ_HILL_VALLEY)
        // We can set the changeAmount property of MutableClock 
        // in the constructor or by setting the changeAmount property. 
        // Now when we invoke next/previous (++/--)
        // the clock moves by the specified amount.
        def clock = 
            new MutableClock(
                defaultTime.toInstant(), 
                defaultTime.zone, 
                Duration.ofDays(18))

        and:
        postOffice = new WesternUnion(clock)

        expect:
        postOffice.acceptLetter(DELIVERY_DATE, MESSAGE) == 1

        when:
        int numberOfLetters = postOffice.deliver()

        then: "Delivery date has not passed so 1 letter"
        numberOfLetters  == 1

        and: "Move clock forward by given amount (18 days) and try to deliver"
        // The ++/-- operators are mapped to next/previous. 
        // Amount cannot be months or years, then we need the adjust method.
        clock++

        when: "Try to deliver now"
        int newNumberOfLetters = postOffice.deliver()

        then: "Delivery date has passed now so 0 letters left"
        newNumberOfLetters == 0
    }
}

Written with Spock 2.0.

September 10, 2019

Spocklight: Use Stub or Mock For Spring Component Using @SpringBean

When we write tests or specifications using Spock for our Spring Boot application, we might want to replace some Spring components with a stub or mock version. With the stub or mock version we can write expected outcomes and behaviour in our specifications. Since Spock 1.2 and the Spock Spring extension we can use the @SpringBean annotation to replace a Spring component with a stub or mock version. (This is quite similar as the @MockBean for Mockito mocks that is supported by Spring Boot). We only have to declare a variable in our specification of the type of the Spring component we want to replace. We directly use the Stub() or Mock() methods to create the stub or mock version when we define the variable. From now on we can describe expected output values or behaviour just like any Spock stub or mock implementation.

To use the @SpringBean annotation we must add a dependency on spock-spring module to our build system. For example if we use Gradle we use the following configuration:

...
dependencies {
    ...
    testImplementation("org.spockframework:spock-spring:1.3-groovy-2.5")
    ...
}
...

Let's write a very simple Spring Boot application and use the @SpringBean annotation to create a stubbed component. First we write a simple interface with a method that accepts an argument of type String and return a new String value:

package mrhaki.spock;

public interface MessageComponent {
    String hello(final String name);
}

Next we use this interface in a Spring REST controller where we use constructor dependency injection to inject the correct implementation of the MessageComponent interface into the controller:

package mrhaki.spock;

import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class MessageController {

    private final MessageComponent messageComponent;

    public MessageController(final MessageComponent messageComponent) {
        this.messageComponent = messageComponent;
    }

    @GetMapping(path = "/message", produces = MediaType.TEXT_PLAIN_VALUE)
    public String message(@RequestParam final String name) {
        return messageComponent.hello(name);
    }

}

To test the controller we write a new Spock specification. We use Spring's MockMvc support to test our controller, but the most important part in the specification is the declaration of the variable messageComponent with the annotation @SpringBean. Inside the method where we invoke /message?name=mrhaki we use the stub to declare our expected output:

package mrhaki.spock

import org.spockframework.spring.SpringBean
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest
import org.springframework.test.web.servlet.MockMvc
import spock.lang.Specification

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get

@WebMvcTest(MessageController)
class MessageControllerSpec extends Specification {

    @Autowired
    private MockMvc mockMvc

    /**
     * Instead of the real MessageComponent implementation
     * in the application context we want to use our own
     * Spock Stub implementation to have control over the
     * output of the message method.
     */
    @SpringBean
    private MessageComponent messageComponent = Stub()

    void "GET /message?name=mrhaki should return result of MessageComponent using mrhaki as argument"() {
        given: 'Stub returns value if invoked with argument "mrhaki"'
        messageComponent.hello("mrhaki") >> "Hi mrhaki"

        when: 'Get response for /message?name=mrhaki'
        final response = mockMvc.perform(get("/message").param("name", "mrhaki"))
                                .andReturn()
                                .getResponse()

        then:
        response.contentAsString == "Hi mrhaki"
    }
}

Written with Spock 1.3-groovy-2.5 and Spring Boot 2.1.8.RELEASE.

October 2, 2017

Spocklight: Reuse Variables In Data Providers

Writing a parameterized specification in Spock is easy. We need to add the where: block and use data providers to specify different values. For each set of values from the data providers our specifications is run, so we can test for example very effectively multiple input arguments for a method and the expected outcome. A data provider can be anything that implements the Iterable interface. Spock also adds support for a data table. In the data table we define columns for each variable and in the rows values for each variable. Since Spock 1.1 we can reuse the value of the variables inside the data provider or data table. The value of the variable can only be reused in variables that are defined after the variable we want to reuse is defined.

In the following example we have two feature methods, one uses a data provider and one a data table. The variable sentence is defined after the variable search, so we can use the search variable value in the definition of the sentence variable.

package mrhaki

import spock.lang.Specification
import spock.lang.Unroll

class CountOccurrencesSpec extends Specification {

    @Unroll('#sentence should have #count occurrences of #search (using data table)')
    void 'count occurrences of text using data table'() {
        expect:
        sentence.count(search) == count

        where:
        search  | sentence                                                  || count
        'ABC'   | "A simple $search"                                        || 1
        'Spock' | "Don't confuse $search framework, with the other $search" || 2
    }

    @Unroll('#sentence should have #count occurrences of #search (using data provider)')
    void 'count occurrences of text using data provider'() {
        expect:
        sentence.count(search) == count

        where:
        search << ['ABC', 'Spock']
        sentence << ["A simple $search", "Don't confuse $search framework, with the other $search"]
        count << [1, 2]
    }
}

When we run the specification the feature methods will pass and in we see in the @Unroll descriptions that the sentence variable uses the value of search:

Written with Spock 1.1-groovy-2.4.

September 18, 2017

Spocklight: Group Assertions With verifyAll

We all know writing tests or specifications with Spock is fun. We can run our specifications and when one of our assertions in a feature method invalid, the feature method is marked as failed. If we have more than one assertion in our feature method, only the first assertion that fails is returned as an error. Any other assertion after a failing assertion are not checked. To let Spock execute all assertions and return all failing assertions at once we must use the verifyAll method. We pass a closure with all our assertions to the method. All assertions will be checked when use the verifyAll and if one or more of the assertions is invalid the feature method will fail.

In the following example specification we have 3 assertions in the feature method check all properties are valid. We don't use the verifyAll method in our first example.

// File: verifyall.groovy
@Grab('org.spockframework:spock-core:1.1-groovy-2.4')
import spock.lang.Specification
import spock.lang.Subject

class CourseSpec extends Specification {

    @Subject
    private course = new Course()
    
    void 'check all properties are valid'() {
        given:
        course.with {
            name = 'G'
            location = 'T'
            numberOfDays = 0
        }
    
        expect:
        with(course) {
            name.size() > 2
            location.size() > 2
            numberOfDays > 0
        }
    }

}

class Course {
    String name
    String location
    int numberOfDays
}

Let's see what happens when we don't use the verifyAll method and run the specification:

$ groovy verifyall.groovy
JUnit 4 Runner, Tests: 1, Failures: 1, Time: 28
Test Failure: check all properties are valid(CourseSpec)
Condition not satisfied:

name.size() > 2
|    |      |
G    1      false

 at CourseSpec.check all properties are valid_closure2(verifyall.groovy:21)
 at groovy.lang.Closure.call(Closure.java:414)
 at spock.lang.Specification.with(Specification.java:186)
 at CourseSpec.check all properties are valid(verifyall.groovy:20)

In the next example we use verifyAll to group the assertions for our course object:

// File: verifyall.groovy
@Grab('org.spockframework:spock-core:1.1-groovy-2.4')
import spock.lang.Specification
import spock.lang.Subject


class CourseSpec extends Specification {

    @Subject
    private course = new Course()
    
    void 'check all properties are valid'() {
        given:
        course.with {
            name = 'G'
            location = 'T'
            numberOfDays = 0
        }
    
        expect:
        with(course) {
            verifyAll {
                name.size() > 2
                location.size() > 2
                numberOfDays > 0
            }
        }
    }

}

class Course {
    String name
    String location
    int numberOfDays
}

We re-run the specification and now we get different output with all three assertions mentioned:

$ groovy verifyall.groovy
JUnit 4 Runner, Tests: 1, Failures: 1, Time: 26
Test Failure: check all properties are valid(CourseSpec)
Condition failed with Exception:

with(course) { verifyAll { name.size() > 2 location.size() > 2 numberOfDays > 0 } }

 at CourseSpec.check all properties are valid(verifyall.groovy:20)
Caused by: org.junit.runners.model.MultipleFailureException: There were 3 errors:
  org.spockframework.runtime.ConditionNotSatisfiedError(Condition not satisfied:

name.size() > 2
|    |      |
G    1      false
)
  org.spockframework.runtime.ConditionNotSatisfiedError(Condition not satisfied:

location.size() > 2
|        |      |
T        1      false
)
  org.spockframework.runtime.ConditionNotSatisfiedError(Condition not satisfied:

numberOfDays > 0
|            |
0            false
)
 at CourseSpec.$spock_feature_0_0_closure2$_closure3(verifyall.groovy:24)
 at CourseSpec.$spock_feature_0_0_closure2$_closure3(verifyall.groovy)
 at groovy.lang.Closure.call(Closure.java:414)
 at spock.lang.Specification.verifyAll(Specification.java:223)
 at CourseSpec.check all properties are valid_closure2(verifyall.groovy:21)
 at groovy.lang.Closure.call(Closure.java:414)
 at spock.lang.Specification.with(Specification.java:186)
 ... 1 more

Written with Spock 1.1-groovy-2.4.

June 2, 2017

Spocklight: Indicate Specification As Pending Feature

Sometimes we are working on a new feature in our code and we want to write a specification for it without yet really implementing the feature. To indicate we know the specification will fail while we are implementing the feature we can add the @PendingFeature annotation to our specification method. With this annotation Spock will still execute the test, but will set the status to ignored if the test fails. But if the test passes the status is set to failed. So when we have finished the feature we need to remove the annotation and Spock will kindly remind us to do so this way.

In the following example specification we use the @PendingFeature annotation:

package sample

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

class SampleSpec extends Specification {

    @Subject
    private final converter = new Converter()

    @PendingFeature
    void 'new feature to make String upper case'() {
        given:
        def value = 'Spock is awesome'

        expect: // This will fail as expected
        converter.upper(value) == 'SPOCK IS AWESOME'
    }

}

class Converter {
    String upper(String value) {
        value
    }
}

When we run our test in for example Gradle we get the following result:

Now let's implement the upper method:

package sample

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

class SampleSpec extends Specification {

    @Subject
    private final converter = new Converter()

    @PendingFeature
    void 'new feature to make String upper case'() {
        given:
        def value = 'Spock is awesome'

        expect: // This will fail no more
        converter.upper(value) == 'SPOCK IS AWESOME'
    }

}

class Converter {
    String upper(String value) {
        value.toUpperCase()
    }
}

We run the test again and now we get a failing result although our implementation of the upper method is correct:

So this tells us the @PendingFeature is no longer needed. We can remove it and the specification will pass correctly.

Written with Spock 1.1.

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.

April 10, 2017

Spocklight: Ignoring Other Feature Methods Using @IgnoreRest

To ignore feature methods in our Spock specification we can use the annotation @Ignore. Any feature method or specification with this annotation is not invoked when we run a specification. With the annotation @IgnoreRest we indicate that feature methods that do not have this annotation must be ignored. So any method with the annotation is invoked, but the ones without aren't. This annotation can only be applied to methods and not to a specification class.

In the next example we have a specification with two feature methods that will be executed and one that is ignored:

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

class SampleSpec extends Specification {

    @Subject
    private final underTest = new Sample()

    @IgnoreRest
    void 'run this spec'() {
        expect:
        underTest.message('Asciidoctor') == 'Asciidoctor is awesome.'
    }
    
    @IgnoreRest
    void 'run this spec also'() {
        expect:
        underTest.message('Groovy') == 'Groovy is awesome.'
    }

    void 'ignore this spec'() {
        expect:
        underTest.message('Word') == 'Word is awesome'
    }
    
}

class Sample {
    String message(String tool) {
        println "Getting message for $tool"
        "$tool is awesome."
    }
}

We can run this specification directly from the command line:

$ groovy SampleSpec.groovy
Getting message for Asciidoctor
Getting message for Groovy
JUnit 4 Runner, Tests: 2, Failures: 0, Time: 54
$

Written with Spock 1.0 and Groovy 2.4.10.

November 11, 2016

Spocklight Notebook Is Updated

There is a new update for the Spocklight Notebook.
If you've downloaded the book before, you can download the latest version. The book is free, so you can download it if you didn't download it before.

The new version has one new section:

  • Custom Default Responses for Stubs

September 30, 2016

Spocklight: Check No Exceptions Are Thrown At All

In a previous post we learned that we can check a specific exception is not thrown in our specification with the notThrown method. If we are not interested in a specific exception, but just want to check no exception at all is thrown, we must use the noExceptionThrown method. This method return true if the called code doesn't throw an exception.

In the following example we invoke a method (cook) that can throw an exception. We want to test the case when no exception is thrown:

package com.mrhaki.blog

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

class RecipeServiceSpec extends Specification {
    def """If the recipe is cooked on the right device and
           for the correct amount of minutes,
           then no exception is thrown"""() {
        setup:
        def recipeService = new RecipeService()
        def recipe = new Recipe(device: 'oven', time: 30)

        when:
        recipeService.cook(recipe, 30, 'oven')

        then: 'no exceptions must be thrown'
        noExceptionThrown()
    }
}

class RecipeService {
    def cook(Recipe recipe, int minutes, String device) {
        if (minutes > recipe.time) {
            throw new BurnedException()
        }
        if (device != recipe.device) {
            throw new InvalidDeviceException("Please use $recipe.device for this recipe.")
        }
    }
}

class Recipe {
    int time
    String device
}

import groovy.transform.InheritConstructors

@InheritConstructors
class BurnedException extends RuntimeException {
}

@InheritConstructors
class InvalidDeviceException extends RuntimeException {
}

Written with Spock 1.0-groovy-2.4.

September 16, 2016

Spocklight: Custom Default Responses for Stubs

Although I couldn't make it to Gr8Conf EU this year, I am glad a lot of the presentations are available as slide decks and videos. The slide deck for the talk Interesting nooks and crannies of Spock you (may) have never seen before by Marcin ZajÄ…czkowski is very interesting. This is really a must read if you use Spock (and why shouldn't you) in your projects. One of the interesting things is the ability to change the response for methods in a class that is stubbed using Spock's Stub method, but have no explicit stubbed method definition.

So normally when we create a stub we would add code that implements the methods from the stubbed class. In our specification the methods we have written are invoked instead of the original methods from the stubbed class. By default if we don't override a method definition, but it is used in the specification, Spock will try to create a response using a default response strategy. The default response strategy for a stub is implemented by the class EmptyOrDummyResponse. For example if a method has a return type Message then Spock will create a new instance of Message and return it to be used in the specification. Spock also has a ZeroOrNullResponse response strategy. With this strategy null is returned for our method that returns the Message type.

Both response strategies implement the IDefaultResponse interface. We can write our own response strategy by implementing this interface. When we use the Stub method we can pass an instance of our response strategy with the defaultResponse named argument of the method. For example: MessageProvider stub = Stub(defaultResponse: new CustomResponse()). We implement the respond
method of IDefaultResponse to write a custom response strategy. The method gets a IMockInvocation instance. We can use this instance to check for example the method name, return type, arguments and more. Based on this we can write code to return the response we want.

In the following example we have a Spock specification where we create a stub using the default response strategy, the ZeroOrNullResponse strategy and a custom written response strategy:

package com.mrhaki.spock

@Grab('org.spockframework:spock-core:1.0-groovy-2.4')
import spock.lang.Specification
import spock.lang.Subject
import org.spockframework.mock.ZeroOrNullResponse
import org.spockframework.mock.IDefaultResponse
import org.spockframework.mock.IMockInvocation

class SampleSpec extends Specification {

    def """stub default response returns
           instance of Message created with default constructor"""() {
        given: 'Use default response strategy EmptyOrDummyResponse'
        final MessageProvider messageProvider = Stub()
        final Sample sample = new Sample(messageProvider)

        expect:
        sample.sampleMessage == 'Sample says: default'
    }

    def "stub default reponse returns null with ZeroOrNullResponse"() {
        given: 'Use default response strategy of ZeroOrNullResponse'
        final MessageProvider messageProvider =
                Stub(defaultResponse: ZeroOrNullResponse.INSTANCE)
        final Sample sample = new Sample(messageProvider)

        when:
        sample.sampleMessage

        then: 'messageProvider.message returns null'
        thrown(NullPointerException)
    }

    def """stub default response returns
           Message object with initialized text property
           from StubMessageResponse"""() {
        given: 'Use custom default response strategy'
        final MessageProvider messageProvider =
                Stub(defaultResponse: new StubMessageResponse())
        final Sample sample = new Sample(messageProvider)

        expect:
        sample.sampleMessage == 'Sample says: *STUB MESSAGE TEXT*'
    }

}

/**
 * Class to test with a dependency on MessageProvider
 * that is stubbed in the specification.
 */
class Sample {
    private final MessageProvider messageProvider

    Sample(final MessageProvider messageProvider) {
        this.messageProvider = messageProvider
    }

    String getSampleMessage() {
        "Sample says: ${messageProvider.message.text}"
    }

    String sampleMessage(String prefix) {
        "Sample says: ${messageProvider.getMessageWithPrefix(prefix).text}"
    }
}

/**
 * Work with messages. This interface is stubbed
 * in the specification.
 */
interface MessageProvider {
    Message getMessage()
    Message getMessageWithPrefix(String prefix)
}

/**
 * Supporting class for MessageProvider.
 */
class Message {
    String text = 'default'
}

/**
 * Custom default response strategy.
 * When a method has a Message return type then we
 * create an instance of Message with a custom text
 * property value.
 * Otherwise rely on default behaviour.
 */
class StubMessageResponse implements IDefaultResponse {
    @Override
    Object respond(IMockInvocation invocation) {
        // If return type of method is Message we create
        // a new Message object with a filled text property.
        if (invocation.method.returnType == Message) {
            return new Message(text: '*STUB MESSAGE TEXT*')
        }

        // Otherwise use default response handler for Stubs.
        return ZeroOrNullResponse.INSTANCE.respond(invocation)
    }
}

Written with Spock 1.0-groovy-2.4.

February 1, 2016

Spocklight Notebook Is Updated

I've written a couple of new blog posts about Spock the last couple of months, so it was time to also update the Spocklight Notebook. If you've downloaded the book before, you can download the latest version. The book is free, so you download it if you didn't download it before. The following subjects have been added to the new version:

  • Mocks And Stubs Returning Sequence of Values
  • Grouping Assertions

January 29, 2016

Spocklight: Grouping Assertions

In a Spock specification we write our assertion in the then: or expect: blocks. If we need to write multiple assertions for an object we can group those with the with method. We specify the object we want write assertions for as argument followed by a closure with the real assertions. We don't need to use the assert keyword inside the closure, just as we don't have to use the assert keyword in an expect: or then: block.

In the following example specification we have a very simple implementation for finding an User object. We want to check that the properties username and name have the correct value.

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

class UserServiceSpec extends Specification {

    @Subject
    private UserService userService = new DefaultUserService()
    
    def "check user properties"() {
        when:
        final User user = userService.findUser("mrhaki")
        
        then:
        // Assert each property.
        user.username == "mrhaki"
        user.name == "Hubert A. Klein Ikkink"
    }

    def "check user properties using with()"() {
        when:
        final User user = userService.findUser("mrhaki")
        
        then:
        // Assert using with().
        with(user) {
            username == "mrhaki"
            name == "Hubert A. Klein Ikkink"
        }
    }

    def "check expected user properties using with()"() {
        expect:
        with(userService.findUser("mrhaki")) {
            username == "mrhaki"
            name == "Hubert A. Klein Ikkink"
        }
    }
    

}

interface UserService {
    User findUser(final String username)    
}

class DefaultUserService implements UserService {
    User findUser(final String username) {
        new User(username: "mrhaki", name: "Hubert A. Klein Ikkink")
    }
}

class User {
    String username
    String name
}

Written with Spock 1.0.

September 21, 2015

Spocklight: Mocks And Stubs Returning Sequence of Values

Creating and working with mocks and stubs in Spock is easy. If we want to interact with our mocks and stubs we have several options to return values. One of the options is the triple right shift operator >>>. With this operator we can define different return values for multiple invocations of the stubbed or mocked method. For example we can use the >>> operator followed by a list of return values ['abc', 'def', 'ghi']. On the first invocation abc is return, the second invocation returns def and the third (and following) invocation(s) return ghi.

In the following specification we have a class under test StringUtil. The class has a dependency on an implementation of the Calculator interface. We mock this interface in our specification. We expect the calculateSize method is invoked 5 times, but we only provide 3 values for the invocations. This means the first time 1 is used, the second time 3 is used and the remaining invocations get the value 4:

package com.mrhaki.spock

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

class SampleSpec extends Specification {

    @Subject
    def util = new StringUtil()

    def "Calculate sizes of String values"() {
        given:
        def calculator = Mock(Calculator)
        util.calculator = calculator

        when:
        def total = util.size('one', 'two', 'three', 'four', 'five')

        then:
        5 * calculator.calculateSize(_) >>> [1,3,4]

        total == 1 + 3 + 4 + 4 + 4
    }

}

class StringUtil {
    def calculator

    int size(final String[] s) {
        s.sum { calculator.calculateSize(it) }
    }
}

interface Calculator {
    int calculateSize(final String s)
}

Written with Spock 1.0-groovy-2.4.

September 6, 2015

Spocklight Notebook Is Updated

I've written a couple of new blog posts about Spock the last couple of days, so it was time to also update the Spocklight Notebook. The following subjects have been added to the new version:

  • Including or Excluding Specifications Based On Annotations
  • Optimize Run Order Test Methods
  • Auto Cleanup Resources
  • Undo MetaClass Changes
  • Undo Changes in Java System Properties
  • Only Run Specs Based On Conditions

September 4, 2015

Spocklight: Undo Changes in Java System Properties

If we need to add a Java system property or change the value of a Java system property inside our specification, then the change is kept as long as the JVM is running. We can make sure that changes to Java system properties are restored after a feature method has run. Spock offers the RestoreSystemProperties extension that saves the current Java system properties before a method is run and restores the values after the method is finished. We use the extension with the @RestoreSystemProperties annotation. The annotation can be applied at specification level or per feature method.

In the following example we see that changes to the Java system properties in the first method are undone again in the second method:

package com.mrhaki.spock

import spock.lang.Specification
import spock.lang.Stepwise
import spock.util.environment.RestoreSystemProperties

// Use Stepwise extension so the order
// of method invocation is guaranteed.
@Stepwise
class SysSpec extends Specification {

    // Changes to Java system properties in this
    // method are undone once the method is done.
    @RestoreSystemProperties
    def "first method adds a Java system property"() {
        setup:
        System.properties['spockAdded'] = 'Spock is gr8'

        expect:
        System.properties['spockAdded'] == 'Spock is gr8'
    }

    def "second method has no access to the new property"() {
        expect:
        !System.getProperty('spockAdded')
    }

}

Written with Spock 1.0-groovy-2.4.

Spocklight: Only Run Specs Based On Conditions

In a previous blog post we have seen the IgnoreIf extension. There is also a counterpart: the Requires extension. If we apply this extension to a feature method or specification class than the method or whole class is executed when the condition for the @Requires annotation is true. If the condition is false the method or specification is not executed. As a value for the @Requires annotation we must specify a closure. In the closure Spock adds some properties we can use for our conditions:

  • jvm can be used to check a Java version or compatibility.
  • sys returns the Java system properties.
  • env used to access environment variables.
  • os can be used to check for operating system names.
  • javaVersion has the Java version as BigDecimal, eg. 1.8.

In the following example we use the @Requires annotation with different conditions:

package com.mrhaki.spock

import spock.lang.Requires
import spock.lang.Specification

class RequiresSampleSpec extends Specification {

    @Requires({ Boolean.valueOf(sys['spock.longRunning']) })
    def "run spec if Java system property 'spock.longRunning' is true"() {
        expect:
        true
    }

    @Requires({ Boolean.valueOf(env['SPOCK_LONG_RUNNING']) })
    def "run spec if environment variable 'SPOCK_LONG_RUNNING' is true"() {
        expect:
        true
    }

    @Requires({ javaVersion >= 1.7 })
    def "run spec if run in Java 1.7 or higher"() {
        expect:
        true
    }

    @Requires({ jvm.isJava8() })
    def "run spec if run in Java 1.8"() {
        expect:
        true
    }

    @Requires({ os.isWindows() })
    def "run only if run on windows operating system"() {
        expect:
        true
    }

}

If we have the same condition to be applied for all feature methods in a specification we can use the @Requires annotation at the class level:

package com.mrhaki.spock

import spock.lang.Requires
import spock.lang.Specification

@Requires({ jvm.isJava7Compatible() })
class RequiresSpec extends Specification {

    def "all feature methods run only if JVM is Java 7 compatible"() {
        expect:
        true
    }

}

Written with Spock 1.0-groovy-2.4.