Marcin Erdmann

Groovy, Grails, Geb...

Including and excluding Spock specifications from execution using a configuration file

It's a common requirement to be able to execute just a subset of your tests in a given situation. Or just run the quick tests locally and run everything on CI. And Spock being an excellent testing framework obviously supports it.

In the past I used Spock's @IgnoreIf and @Require built-in extensions to exclude/include certain specs under given conditions. An example usage for marking some tests to be only run as a part of the CI build could be:

@Require({System.properties.containsKey('ciBuild')})
class SlowSpec extends Specification { ... }

@Require annotation takes a closure. When the closure evaluates to true the annotated specification will be executed otherwise it will be ignored. As you might expect it becomes pretty cumbersome to have to copy that closure body all over the place if you have several specifications that you wish to annotate. Not to even mention what would you have to do if the logic in the closure had to change. What I came up with in the past was to create a class that extends from Closure and implements doCall() just as the javadoc for Closure class suggest.

@InheritConstructors
class CiBuild extends Closure<Boolean> {
    Boolean doCall() {
        System.properties.containsKey('ciBuild')
    }
}

@Require(CiBuild)
class SlowSpec extends Specification { ... }

It turns out that there is an easier way of doing this. Spock has a configuration mechanism - by default it looks for a SpockConfig.groovy script in classpath. In that script you can configure the test runner to include and exclude specifications based on how they're annotated as well as on what class they're extending. So given an annotation class:

@Retention(RetentionPolicy.RUNTIME)
@Target([ElementType.TYPE, ElementType.METHOD])
@Inherited
public @interface Slow {
}

And an annotated spec:

@Slow
class SlowSpec extends Specification { ... }

If we create a resource file called SpockConfig.groovy with the following contents:

runner {
    if (!System.properties.containsKey('test.include.slow')) {
        exclude Slow
    }
}

Then by default the SlowSpec will be excluded. On the other hand if you set test.include.slow system property for test execution then all your tests will be executed. You can also be more specific in what you want to skip by only applying the annotation to certain feature methods.

If you don't want to use annotation but want to explicitly exclude some specifications in the configuration file you can use specification class or base class:

runner {
    if (!System.properties.containsKey('test.include.slow')) {
        exclude SlowSpec
    }
}

The only documentation I could find on how to use the configuration file is in javadoc for RunnerConfiguration class. Spock is very powerful but its documentation could use some love...