What’s new in (upcoming) Spock 1.0 – simpler conditional test execution with @Requires and @IgnoreIf

Posted: 2014-09-17 in Tools, Tricks & Tips
Tags: , , , ,


Some tests (especially integration tests) should be run only if certain conditions are (or are not) met. Upcoming Spock 1.0 provides new @Requires and improved @IgnoreIf extensions to handle that requirement in a convenient way.

Historical background – waiting for Spock 1.0

The latest stable Spock version (0.7) has been released in October 2012 (~2 years as the time of writing). There have been made hundreds of commits to Git repository since then and many new features and improvements are already available in the current SNAPSHOT version. Unfortunately the “New and Noteworthy” section in the documentation doesn’t cover those changes and the only way (as I know) to get them know is digging into Git commits. As a Spock user I was curious what new is around the corner, so I did that job which resulted in the presentation at Confitura (in Polish). A as bonus I planned a series of blog entries describing those more interesting features and possibly also an update of Spock documentation.

This is just the first part of the changes in (upcoming) Spock 1.0. There is much more to discover and I will be presenting that in future blog posts.

New extension @Requires

@Requires allows to run given test (or the whole specification) only if given criteria are met.

    @Requires({ System.getProperty("os.arch") == "i386" })
    def "should use native libraries on 32-bit JVM"() { ... }

    @Requires({ env.containsKey("ENABLE_CRM_INTEGRATION_TESTS") })
    def "should do complicated things with CRM"() { ... }

It is opposite to already known from Spock 0.7 @Ignore extension which allowed to ignore the given test (or the whole specification) using access to system properties (propertiesSystem.getProperties()), environment variables (envSystem.getenv()) and java version (javaVersionSystem.getProperty("java.version")). In Spock 1.0 branch they both are much more powerful.

Testing my examples in practice I was surprised that a system property “os.arch” returns not a processor architecture taken from an OS, but a JVM version. So, be aware that running a 32-bit JVM on 64-bit system will return “i386”. Hopefully usually that knowledge is not important (unless for example the native libraries are used).

New features in @Requires and @IgnoreIf

In addition to the new @Requires extension it and its twin @IgnoreIf have got new internal abstraction to simplify the way how the operating system information and a JVM version can be accessed.

Operating system

Operating system information can be accessible in Spock 0.7 using os.name and os.version system properties. Unfortunately it can be complicated and error prone in some cases:

    //Run only on Linux and MacOS - in Spock 0.7 style
    @Requires({ System.getProperty("os.name").toLowerCase().contains("mac os") || 
                System.getProperty("os.name").toLowerCase().contains("darwin") || 
                System.getProperty("os.name").toLowerCase().contains("linux")  })
    def "should use fancy text console features"() { ... }

In the meantime Spock has got the new OperatingSystem abstraction which provides methods like:

    String getName() //Linux
    String getVersion() //8.1
    Family getFamily() //SOLARIS (enum)

    boolean isLinux()
    boolean isWindows()
    boolean isMacOs() //also Solaris and Other
    boolean iSolaris()
    boolean isOther()

With their help mentioned example can be simplified to very readable code precisely explaining our intentions:

    @Requires({ os.linux || os.macOs  })    //Spock 1.0 edition
    def "should use fancy text console features"() { ... }

JVM version

In addition to OperatatingSystem there is also Jvm abstraction which provides methods like:

    boolean isJava7() //true only if Java 7
    boolean isJava8Compatible() //true if Java 8+

or more generic:

    String getJavaVersion() //e.g. "1.8.0_05"
    String getJavaSpecificationVersion() //e.g. "1.8"

The original JVM version dependent test cases declaration:

    @IgnoreIf({ javaVersion < 1.8 })
    def "should find at runtime and use CompletableFuture for Java 8+"() { 

can be replaced with more verbose:

    @IgnoreIf({ !jvm.java8Compatible })
    def "should find at runtime and use CompletableFuture for Java 8+"() { 

Using static methods

@Requires (the same as @IgnoreIf) can also use static methods to define a condition (which is available, but less known in Spock 0.7). Those methods can be declared inside a given class, in an another class or (when using Groovy 2.3+) in a trait.

import static FancyFeatureRequiredIntranetConnectionSpecIT.isUrlAvailable

@Requires({ isUrlAvailable("http://some-host.intranet/") })
class FancyFeatureRequiredIntranetConnectionSpecIT extends Specification {

    //could be also from trait or other class
    static boolean isUrlAvailable(String url) { 

    def "should do one thing in intranet"() { ... }

    def "should do another thing in intranet"() { ... }


Please pay attention to the static import in this example. Even though that code without an import looks ok and Idea sees the method it will fail at runtime with groovy.lang.MissingMethodException: No signature of method: (...). This is caused by the way how Spock internally tries to resolve references in a Closure. For detailed explanation see the next paragraph.

groovy.lang.MissingMethodException: No signature of method:
is applicable for argument types: (java.lang.String) values: [http://some-host.intranet/]

How does it internally work (for the curious)?

You may wonder how that code even compile if jvm or os are not known for the Specification class. The key thing is that code inside @Requires (and @IgnoreIf) annotation is wrapped with {} (curly brackets). In Groovy that syntax means “anonymous code block” and it is named Closure. The @Requires annotation accepts a Closure as a parameter:

@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Requires {
  Class<? extends Closure> value(); // <- important line

Two important aspects of a Groovy Closure are used in Spock to implement those features:

  • a block of code is executed “at the later point”
  • an execution context can be delegated

The first point causes that Groovy compiler ignores unresolved references. At the compilation time the execution context can be unknown (a Closure can be passed as a parameter far away from the declaration place). The used references will be resolved at runtime and here the second point applies.

By default property references and methods are attempted to be resolved to the owner (an enclosing class or a surrounding Closure) and later to the delegate (by default the same as owner). A delegate can be changed and that is used in RequiresExtension:

    condition.setDelegate(new PreconditionContext());
    return condition.call();

PreconditionContext is a delegate class for @Requires and @IgnoreIf providing an execution context. Its getOS() or getJvm() methods are resolved and executed when os or jvm properties are used in the annotation Closure. In addition to set a dedicated delegate, a resolve strategy is changed to not bother with an enclosing class and try resolve unresolved references only using PreconditionContext.

If you are new to Groovy and feel confused with the internals I propose you to take a look at the introduction to Groovy Closure. If you like it there is a lot more things to get know about Groovy Closure and its power.

Where can I find Spock 1.0?

Please notice that blog post is about features available in Git branch planned to become Spock 1.0 sometime in the future. They are currently available only in a 1.0-SNAPSHOT version in a separate repository. As not being released (as the time of writing) they could look different or even (in the extreme case) be not available in the final version. Be aware.

To add Spock 1.0 as a dependency to your project define snapshot repository http://oss.sonatype.org/content/repositories/snapshots/ and the Spock artifact in version 1.0-groovy-2.0-SNAPSHOT. Sample configuration in Gradle:

repositories {
    maven { url "http://oss.sonatype.org/content/repositories/snapshots/" }

dependencies {
    testCompile 'org.spockframework:spock-core:1.0-groovy-2.0-SNAPSHOT'


New @Requires extension and the enhancements in @IgnoreIf are the first part of the series about new and noteworthy in upcoming Spock 1.0. When will it be released? Citing John Carmack “It’ll be done when it’s done”. If you would like to bring the release closer donate your time and contribute to the project.

Post written using Spock 1.0-groovy-2.0-SNAPSHOT. Examples can be cloned from GitHub.

Test quickly and prosper🙂.

  1. […] What’s new in (upcoming) Spock 1.0 – simpler conditional test execution with @Requires a… […]

  2. […] What’s new in (upcoming) Spock 1.0 – simpler conditional test execution with @Requires a… […]

  3. […] Simpler conditional test execution with @Requires and @IgnoreIf […]

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s