Loading...

August 27, 2015

Spocklight: Include or Exclude Specifications Based On Class or Interface

In a previous post we saw how can use the Spock configuration file to include or exclude specifications based on annotations. Instead of using annotations we can also use classes to include or exclude specifications. For example we could have a base specification class DatabaseSpecification. Other specifications dealing with databases extend this class. To include all these specifications we use the DatabaseSpecification as value for the include property for the test runner configuration.

Because Java (and Groovy) doesn't support real multiple inheritance this might be a problem if we already have specifications that extends a base class, but the base class cannot be used as filter for the include and exclude runner configuration. Luckily we can also use an interface as the value for the inclusion or exclusion. So we could simple create a marker interface and implement this interface for these specifications we want to include or exclude from the test execution.

We rewrite the sample from our previous post to show how we can use a marker interface. First we take a look at the interface definition:

package com.mrhaki.spock

interface RemoteSpec {
}

We used the @Remote annotation on a specification method, but an interface is implemented on class. Therefore we must refactor our specification into three classes. First we create a base specification class:

package com.mrhaki.spock

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

abstract class WordRepositorySpec<S extends Access> extends Specification {

    @Subject
    S access

    def "test remote or local access"() {
        expect:
        access.findWords('S') == ['Spock']
    }

}

Next we write two new specification classes that extends this abstract class and each uses a different implementation for the class under test.

package com.mrhaki.spock

class LocalWordRepositorySpec 
        extends WordRepositorySpec<LocalAccess> {

    def setup() {
        access = new LocalAccess()
    }

}
package com.mrhaki.spock

class RemoteWordRepositorySpec 
        extends WordRepositorySpec<RemoteAccess>
        // Implement RemoteSpec marker interface
        implements RemoteSpec {

    def setup() {
        access = new RemoteAccess()
    }

}

Instead of using an annotation we use the marker interface RemoteSpec to include it for our test execution in the following configuration:

import com.mrhaki.spock.RemoteSpec

runner {
    println "Using RemoteSpockConfig"

    // Include only test classes that
    // implement the RemoteSpec interface.
    include RemoteSpec

    // Alternative syntax
    // to only look for classes or interfaces.
    // include {
    //     baseClass RemoteSpec
    // }

    // We can also add a condition in
    // the configuration file.
    // In this case we check for a Java
    // system property and if set the
    // specs with interface RemoteSpec
    // are not run.
    if (System.properties['spock.ignore.Remote']) {
        exclude RemoteSpec
    }
}

If we run the specifications with the same Gradle build file as the previous post we see that with the default test task all specifications are executed:

And when we execute the remoteTest task only the RemoteWordRepositorySpec is executed:

Written with Gradle 2.6 and Spock 1.0-groovy-2.4.

Code is available on Github