Search

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

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 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.

September 3, 2015

Spocklight: Undo MetaClass Changes

Spock has the extension ConfineMetaClassChanges that can be used to encapsulate meta class changes to a feature method or specification. We must apply the annotation @ConfineMetaClassChanges to a feature method or to a whole specification to use this extension. Spock replaces the original meta class with a new one before a feature method is executed. After execution of a feature method the original meta class is used again. We could this by hand using the setup, setupSpec and their counter parts cleanup and cleanupSpec, but using this extension is so much easier. We must specify the class or classes whose meta class changes need to be confined as the value for the annotation.

In the following example we add a new method asPirate to the String class. We apply the @ConfineMetaClassChanges to a method. This means the new method is only available inside the feature method.

package com.mrhaki.spock

import spock.lang.Specification
import spock.lang.Stepwise
import spock.util.mop.ConfineMetaClassChanges

// We use @Stepwise in this specification
// to show that changes in the metaClass
// done in the first feature method do not
// work in the second feature method.
@Stepwise
class PirateTalkSpec extends Specification {

    // After this feature method is finished,
    // the metaClass changes to the given
    // class (String in our case) are reverted.
    @ConfineMetaClassChanges([String])
    def "talk like a pirate"() {
        setup:
        String.metaClass.asPirate = { ->
            return "Yo-ho-ho, ${delegate}"
        }

        expect:
        'mrhaki'.asPirate() == 'Yo-ho-ho, mrhaki'
    }

    // In this feature method we no longer
    // can use the asPirate() method that was
    // added to the metaClass.
    def "keep on talking like a pirate"() {
        when:
        'hubert'.asPirate()

        then:
        thrown(MissingMethodException)
    }

}

In the following example code we apply the @ConfineMetaClassChanges to the whole class. Now we see that the new method asPirate is still available in another feature method, than the one that defined it.

package com.mrhaki.spock

import spock.lang.Specification
import spock.lang.Stepwise
import spock.util.mop.ConfineMetaClassChanges

// We use @Stepwise in this specification
// to show that changes in the metaClass
// done in the first feature method still
// work in the second.
@Stepwise
// If set a class level then the
// changes done to the metaClass of
// the given class (String in our
// example) are reverted after the
// specification is finished.
@ConfineMetaClassChanges([String])
class PirateTalkSpec extends Specification {

    def "talk like a pirate"() {
        setup:
        String.metaClass.asPirate = { ->
            return "Yo-ho-ho, ${delegate}"
        }

        expect:
        'mrhaki'.asPirate() == 'Yo-ho-ho, mrhaki'
    }

    def "keep on talking like a pirate"() {
        expect:
        'hubert'.asPirate() == 'Yo-ho-ho, hubert'
    }
}

This post is very much inspired by this blog post of my colleague Albert van Veen.

Written with Spock 1.0-groovy-2.4.

September 1, 2015

Spocklight: Auto Cleanup Resources

Spcok has a lot of nice extensions we can use in our specifications. The AutoCleanup extension makes sure the close() method of an object is called each time a feature method is finished. We could invoke the close() method also from the cleanup method in our specification, but with the @AutoCleanup annotation it is easier and immediately shows our intention. If the object we apply the annotation to doesn't have a close() method to invoke we can specify the method name as the value for the annotation. Finally we can set the attribute quiet to true if we don't want to see any exceptions that are raised when the close() method (or custom method name, that is specified) is invoked.

In the following example code we have a specification that is testing the WatchService implementation. The implementation also implements the Closeable interface, which means we can use the close() method to cleanup the object properly. We also have a custom class WorkDir with a delete() method that needs to be invoked.

package com.mrhaki.spock

import spock.lang.AutoCleanup
import spock.lang.Specification

import java.nio.file.FileSystems
import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.Paths
import java.nio.file.WatchKey
import java.nio.file.WatchService
import java.util.concurrent.TimeUnit

import static java.nio.file.StandardWatchEventKinds.ENTRY_CREATE

class WatchSpec extends Specification {

    // Use close() method of WatchService
    // when feature method is done.
    @AutoCleanup
    private WatchService watchService

    // Use delete() method of WorkDir class
    // when feature method is done.
    // If the quiet attribute is true, then
    // exceptions from the delete() method are
    // not shown, otherwise exceptions are reported.
    @AutoCleanup(value = 'delete', quiet = true)
    private WorkDir testPath = new WorkDir('test-dir')

    def setup() {
        // Get new watch service.
        watchService = FileSystems.default.newWatchService()

        // Register for events when a new file is created
        // in the testPath directory.
        testPath.path.register(watchService, ENTRY_CREATE)
    }

    def "get notification when file is created"() {
        given:
        final Path testFile = testPath.path.resolve('test-file')
        testFile << 'sample'

        and:
        final WatchKey watchKey = watchService.poll(10, TimeUnit.SECONDS)

        when:
        final events = watchKey.pollEvents()

        then:
        events.size() == 1
        events[0].kind() == ENTRY_CREATE

        cleanup:
        Files.delete(testFile)
    }

}

class WorkDir {

    private final Path path

    WorkDir(final String dir) {
        path = Paths.get(dir)
        Files.createDirectories(path)
    }

    Path getPath() {
        path
    }

    void delete() {
        Files.deleteIfExists(path)
    }

}

Written with Spock 1.0-groovy-2.4.

August 28, 2015

Spocklight: Optimize Run Order Test Methods

Spock is able to change the execution order of test methods in a specification. We can tell Spock to re-run failing methods before successful methods. And if we have multiple failing or successful tests, than first run the fastest methods, followed by the slower methods. This way when we re-run the specification we immediately see the failing methods and could stop the execution and fix the errors. We must set the property optimizeRunOrder in the runner configuration of the Spock configuration file. A Spock configuration file with the name SpockConfig.groovy can be placed in the classpath of our test execution or in our USER_HOME/.spock directory. We can also use the Java system property spock.configuration and assign the filename of our Spock configuration file.

In the following example we have a specification with different methods that can be successful or fail and have different durations when executed:

package com.mrhaki.spock

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

class SampleSpec extends Specification {

    @Subject
    private Sample sample = new Sample()

    def "spec1 - slowly should return name property value"() {
        given:
        sample.name = testValue

        expect:
        sample.slowly() == testValue

        where:
        testValue = 'Spock rules'
    }

    def "spec2 - check name property"() {
        given:
        sample.name = testValue

        expect:
        sample.name == testValue

        where:
        testValue = 'Spock is gr8'
    }

    def "spec3 - purposely fail test at random"() {
        given:
        sample.name = testValues[randomIndex]

        expect:
        sample.name == testValues[0]

        where:
        testValues = ['Spock rules', 'Spock is gr8']
        randomIndex = new Random().nextInt(testValues.size())
    }

    def "spec4 - purposely fail test slowly"() {
        given:
        sample.name = 'Spock is gr8'

        expect:
        sample.slowly() == 'Spock rules'
    }

    def "spec5 - purposely fail test"() {
        given:
        sample.name = 'Spock rules'

        expect:
        sample.name == 'Spock is gr8'
    }
}

class Sample {
    String name

    String slowly() {
        Thread.sleep(2000)
        name
    }
}

Let's run our test where there is no optimised run order. We see the methods are executed as defined in the specification:

Next we create a Spock configuration file with the following contents:

runner {
    println "Optimize run order"
    optimizeRunOrder true
}

If we re-run our specification and have this file in the classpath we already see the order of the methods has changed. The failing tests are at the top and the successful tests are at the bottom. The slowest test method is last:

Another re-run has optimised the order by running the slowest failing test after the other failing tests.

Spock keeps track of the failing and successful methods and their execution time in a file with the specification name in the USER_HOME/.spock/RunHistory directory. To reset the information we must delete the file from this directory.

Written with Spock 1.0-groovy-2.4.

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

Spocklight: Including or Excluding Specifications Based On Annotations

One of the lesser known and documented features of Spock if the external Spock configuration file. In this file we can for example specify which specifications to include or exclude from a test run. We can specify a class name (for example a base specification class, like DatabaseSpec) or an annotation. In this post we see how to use annotations to have some specifications run and others not.

The external Spock configuration file is actually a Groovy script file. We must specify a runner method with a closure argument where we configure basically the test runner. To include specification classes or methods with a certain annotation applied to them we configure the include property of the test runner. To exclude a class or method we use the exclude property. Because the configuration file is a Groovy script we can use everything Groovy has to offer, like conditional statements, println statements and more.

Spock looks for a file named SpockConfig.groovy in the classpath of the test execution and in in the USER_HOME/.spock directory. We can also use the Java system property spock.configuration with a file name for the configuration file.

In the following example we first define a simple annotation Remote. This annotation can be applied to a class or method:

package com.mrhaki.spock

import java.lang.annotation.ElementType
import java.lang.annotation.Retention
import java.lang.annotation.RetentionPolicy
import java.lang.annotation.Target

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

We write a simple Spock specification where we apply the Remote annotation to one of the methods:

package com.mrhaki.spock

import spock.lang.Specification

class WordRepositorySpec extends Specification {

    @Remote  // Apply our Remote annotation.
    def "test remote access"() {
        given:
        final RemoteAccess access = new RemoteAccess()

        expect:
        access.findWords('S') == ['Spock']
    }

    def "test local access"() {
        given:
        final LocalAccess access = new LocalAccess()

        expect:
        access.findWords('S') == ['Spock']
    }

}

Next we create a Spock configuration file:

import com.mrhaki.spock.Remote

runner {
    // This is Groovy script and we 
    // add arbitrary code.
    println "Using RemoteSpockConfig"

    // Include only test classes or test
    // methods with the @Remote annotation
    include Remote

    // Alternative syntax
    // to only look for annotations.
    // include {
    //     annotation Remote
    // }

    
    // 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 @Remote are not run.
    if (System.properties['spock.ignore.Remote']) {
        exclude Remote
    }
}

When we run the WordRepositorySpec and our configuration file is on the classpath only the specifications with the @Remote annotation are executed. Let's apply this in a simple Gradle build file. In this case we save the configuration file as src/test/resources/RemoteSpockConfig.groovy, we create a new test task remoteTest and set the Java system property spock.configuration:

apply plugin: 'groovy'

repositories {
    jcenter()
}

dependencies {
    compile 'org.codehaus.groovy:groovy-all:2.4.4'
    testCompile 'org.spockframework:spock-core:1.0-groovy-2.4'
}

// New test task with specific 
// Spock configuration file.
task remoteTest(type: Test) {
    // This task belongs to Verification task group.
    group = 'Verification'

    // Set Spock configuration file when running
    // this test task.
    systemProperty 'spock.configuration', 'RemoteSpockConfig.groovy'
}

Now when we execute the Gradle test task all specifications are executed:

And when we run remoteTest only the specification with the @Remote annotation are executed:

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

The code is available on Github

February 6, 2015

Spocklight: Capture and Assert System Output

Spock supports JUnit rules out of the box. We simply add a rule with the @Rule annotation to our Spock specification and the rule can be used just like in a JUnit test. The Spring Boot project contains a JUnit rule OutputCapture to capture the output of System.out and System.err.

In the following example specification we apply the OutputCapture rule and use it in two feature methods:

package com.mrhaki.spock

@Grab('org.spockframework:spock-core:0.7-groovy-2.0')
import spock.lang.*

@Grab('org.springframework.boot:spring-boot:1.2.1.RELEASE')
import org.springframework.boot.test.OutputCapture

class CaptureOutputSpec extends Specification {

    @org.junit.Rule
    OutputCapture capture = new OutputCapture()


    def "capture output print method"() {
        when:
        print 'Groovy rocks'

        then:
        capture.toString() == 'Groovy rocks'
    }

    def "banner text must contain given messagen and fixed header"() {
        given:
        final Banner banner = new Banner(message: 'Spock is gr8!')

        when:
        banner.print()

        then:
        final List lines = capture.toString().tokenize(System.properties['line.separator'])
        lines.first() == '*** Message ***'
        lines.last()  == ' Spock is gr8! '
    }

}


/**
 * Class under test. The print method
 * uses println statements to display
 * some message on the console.
 */
class Banner {

    String message 

    void print() {
        println ' Message '.center(15, '*')
        println message.center(15)
    }

}

Written with Spock-0.7-groovy-2.0.

October 28, 2014

Spocklight Notebook is Published

Today Spocklight Notebook is published as a free book. This book is an electronic publication with all Spocklight blog posts about the Spock framework bundled.

The book is published at Leanpub and is available in three formats: PDF, MOBI (for Kindle) and EPUB (for iPad). Updates for the book are also free. So when new Spocklight blog posts will be added to the book you will get those updates for free.

I hope you will enjoy the book and I will keep it up-to-date with new content when I publish new Spocklight blog posts.

October 13, 2014

Spocklight: Indicate Class Under Test with Subject Annotation

If we write a specification for a specific class we can indicate that class with the @Subject annotation. This annotation is only for informational purposes, but can help in making sure we understand which class we are writing the specifications for. The annotation can either be used at class level or field level. If we use the annotation at class level we must specify the class or classes under test as argument for the annotation. If we apply the annotation to a field, the type of the field is used as the class under test. The field can be part of the class definition, but we can also apply the @Subject annotation to fields inside a feature method.

In the following example Spock specification we write a specification for the class Greet. The definition of the Greet class is also in the code listing. We use the @Subject annotation on the field greet to indicate this instance of the Greet class is the class we are testing here. The code also works without the @Subject annotation, but it adds more clarity to the specification.

package com.mrhaki.spock

@Grab('org.spockframework:spock-core:0.7-groovy-2.0')
import spock.lang.*

// The @Subject annotation can also be applied at class level.
// We must specify the class or classes as arguments:
// @Subject([Greet])
class GreetSpec extends Specification {

    // The greet variable of type Greet is the 
    // class we are testing in this specification.
    // We indicate this with the @Subject annotation.
    @Subject 
    private Greet greet = new Greet(['Hi', 'Hello'])

    // Simple specification to test the greeting method.
    def "greeting should return a random salutation followed by name"() {
        when:
        final String greeting = greet.greeting('mrhaki')

        then:
        greeting == 'Hi, mrhaki' || greeting == 'Hello, mrhaki'
    }

}

/**
 * Class which is tested in the above specification.
 */
@groovy.transform.Immutable
class Greet {

    final List<String> salutations

    String greeting(final String name) {
        final int numberOfSalutations = salutations.size()
        final int selectedIndex = new Random().nextInt(numberOfSalutations)
        final String salutation = salutations.get(selectedIndex)

        "${salutation}, ${name}"
    }

}

Code written with Spock 0.7-groovy-2.0 and Groovy 2.3.7.

June 25, 2014

Spocklight: Write Our Own Data Provider

We can use data pipes to write data driven tests in Spock. A data pipe (<<) is fed by a data provider. We can use Collection objects as data provider, but also String objects and any class that implements the Iterable interface. We can write our own data provider class if we implement the Iterable interface.

In the following sample code we want to test the female property of the User class. We have the class MultilineProvider that implements the Iterable interface. The provider class accepts a multiline String value and returns the tokenized result of each line in each iteration.

package com.mrhaki.spock

@Grab('org.spockframework:spock-core:0.7-groovy-2.0')
import spock.lang.*

class ProviderSampleSpec extends Specification {

    @Unroll("Gender #gender for #name is #description")
    def "check if user is female or male based on gender value"() {
        given:
        def userData = '''\
            1;mrhaki;M;false
            2;Britt;F;true'''

        expect:
        new User(name: name, gender: gender).female == Boolean.valueOf(expected)

        where:
        [_, name, gender, expected] << new MultilineProvider(source: userData)

        // Extra data variable to be used in
        // @Unroll description.
        description = expected ? 'female' : 'not female'
    }

}

/**
 * Class under test. 
 */
class User {
    String name, gender

    Boolean isFemale() {
        gender == 'F'
    }
}

/**
 * Class implements Iterable interface so 
 * it can be used as data provider.
 */
class MultilineProvider implements Iterable {
    def source
    def lines
    def separator = ';'

    private int counter

    /**
     * Set multiline String as source 
     * and transform to a List of String
     * values and assign to the lines
     * property.
     */
    void setSource(source) {
        this.source = source.stripIndent()
        lines = this.source.readLines()
    }

    @Override
    Iterator iterator() {
        [
            hasNext: { 
                counter < lines.size() 
            },
            next: { 
                lines[counter++].tokenize(separator)
            }
        ] as Iterator
    }
}

Code written with Spock 0.7-groovy-2 and Groovy 2.3.3.

Spocklight: Assign Multiple Data Variables from Provider

We can write data driven tests with Spock. We can specify for example a data table or data pipes in a where: block. If we use a data pipe we can specify a data provider that will return the values that are used on each iteration. If our data provider returns multiple results for each row we can assign them immediately to multiple variables. We must use the syntax [var1, var2, var3] << providerImpl to assign values to the data variables var1, var2 and var3. We know from Groovy the multiple assignment syntax with parenthesis ((var1, var2, var3)), but with Spock we use square brackets.

In the following sample specification we have a simple feature method. The where: block shows how we can assign the values from the provider to multiple data variables. Notice we can skip values from the provider by using a _ to ignore the value.

package com.mrhaki.spock

@Grab('org.spockframework:spock-core:0.7-groovy-2.0')
import spock.lang.*

class MultiDataVarSpec extends Specification {

    @Unroll("#value as upper case is #expected")
    def "check upper case value of String"() {
        expect:
        value.toUpperCase() == expected

        where:
        // Multi data variables value and expected,
        // will be filled with elements from a row
        // on each iteration. The first element of each
        // row is ignored.
        // E.g. on first iteration:
        // value = 'abc'
        // and expected = 'ABC'
        [_, value, expected] << [
            [1, 'abc', 'ABC'],
            [2, 'def', 'DEF'], 
            [3, '123', '123']
        ]
    }


}

Code written with Spock 0.7-groovy-2.0 and Groovy 2.3.3.

June 20, 2014

Spocklight: Ignore Specifications Based On Conditions

We can use the @Ignore and @IgnoreRest annotation in our Spock specifications to not run the annotated specifications or features. With the @IgnoreIf annotation we can specify a condition that needs to evaluate to true to not run the feature or specification. The argument of the annotation is a closure. Inside the closure we can access three extra variables: properties (Java system properties), env (environment variables) and javaVersion.

In the following Spock specification we have a couple of features. Each feature has the @IgnoreIf annotation with different checks. We can use the extra variables, but we can also invoke our own methods in the closure argument for the annotation:

package com.mrhaki.spock

import spock.lang.*

class SampleRequiresSpec extends Specification {

    private static boolean isOsWindows() {
        System.properties['os.name'] == 'windows'
    }

    @IgnoreIf({ Boolean.valueOf(properties['spock.ignore.longRunning']) })
    def "run spec if Java system property 'spock.ignore.longRunning' is not set or false"() {
        expect:
        true
    }

    @IgnoreIf({ Boolean.valueOf(env['SPOCK_IGNORE_LONG_RUNNING']) })
    def "run spec if environment variable 'SPOCK_IGNORE_LONG_RUNNING' is not set or false"() {
        expect:
        true
    }

    @IgnoreIf({ javaVersion < 1.7 })
    def "run spec if run in Java 1.7 or higher"() {
        expect:
        true
    }

    @IgnoreIf({ javaVersion != 1.7 })
    def "run spec if run in Java 1.7"() {
        expect:
        true
    }

    @IgnoreIf({ isOsWindows() })
    def "run only if run on non-windows operating system"() {
        expect:
        true
    }

}

When we run our specification with Java 1.8 and do not set the Java system property spock.ignore.longRunning or we set the value to false and we do not set the environment variable SPOCK_IGNORE_LONG_RUNNING or give it the value false we can see that some features are ignored:

Now we run on Java 1.7, Windows operating system and set the Java system property spock.ignore.longRunning with the value true and the environment variable SPOCK_IGNORE_LONG_RUNNING with the value true. The resulting report shows the specifications that are ignored and those that are executed:

Code written with Spock 0.7-groovy-2.

June 16, 2014

Spocklight: Extra Data Variables for Unroll Description

Spock's unroll feature is very powerful. The provider data variables can be used in the method description of our specification features with placeholders. For each iteration the placeholders are replaced with correct values. This way we get a nice output where we immediately can see the values that were used to run the code. Placeholders are denoted by a hash sign (#) followed by the variable name. We can even invoke no-argument methods on the variable values or access properties. For example if we have a String value we could get the upper case value with #variableName.toUpperCase(). If we want to use more complex expressions we must introduce a new data variable in the where block. The value of the variable will be determined for each test invocation and we can use the result as a value in the method description.

package com.mrhaki.spock

import spock.lang.*

class SampleSpec extends Specification {

    @Unroll
    def "check if '#value' is lower case"() {
        expect:
        value.every { (it as char).isLowerCase() } == result

        where:
        value || result
        'A'   || false
        'Ab'  || false
        'aB'  || false
        'a'   || true
        'ab'  || true
    }

}

If we look at the output of the tests we see the method names are not really representing the code we test. For example we can not see if the value was lower case or not.

We rewrite the specification and add a new data variable unrollDescription in the where block. We then refer to this variable in our method name description.

package com.mrhaki.spock

import spock.lang.*

class SampleSpec extends Specification {

    @Unroll
    // Alternatively syntax as 
    // unroll annotation argument:
    // @Unroll("'#value' is #unrollDescription")
    def "'#value' is #unrollDescription"() {
        expect:
        value.every { (it as char).isLowerCase() } == result

        where:
        value || result
        'A'   || false
        'Ab'  || false
        'aB'  || false
        'a'   || true
        'ab'  || true

        unrollDescription = result ? 'lower case' : 'not lower case'
    }

}

When we look at the output we now have more descriptive method names:

This post is inspired by the great Idiomatic Spock talk by Rob Fletcher at Gr8Conf 2014 Europe.

Code written with Spock 0.7 for Groovy 2.

August 22, 2013

Spocklight: Using the Old Method

Spock has some great features to write specifications or tests that are short and compact. One of them is the old() method. The old() method can only be used in a then: block. With this method we get the value a statement had before the when: block is executed.

Let's see this with a simple example. In the following specification we create a StringBuilder with an initial value. In the then: block we use the same initial value for the assertion:

package com.mrhaki.spock 

class SampleSpec extends spock.lang.Specification {

    def "after addition of a new value the content is the initial value with the appended value"() {
        given:
        final StringBuilder builder = new StringBuilder('Spock ')

        when:
        builder << appendValue

        then:
        builder.toString() == 'Spock ' + appendValue

        where:
        appendValue << ['rocks!', 'is fun.', 'amazes.']
    }

}

If we want to change the initial value when we create the StringBuilder we must also change the assertion. We can refactor the feature method and show our intention of the specification better. We add the variable oldToString right after we have created the StringBuilder. We use this in the assertion.

package com.mrhaki.spock 

class SampleSpec extends spock.lang.Specification {

    def "after addition of a new value the content is the initial value with the appended value"() {
        given:
        final StringBuilder builder = new StringBuilder('Spock ')
        final String oldToString = builder.toString()

        when:
        builder << appendValue

        then:
        builder.toString() == oldToString + appendValue

        where:
        appendValue << ['rocks!', 'is fun.', 'amazes.']
    }

}

But with Spock we can do one better. Instead of creating an extra variable we can use the old() method. In the assertion we replace the variable reference oldToString with old(builder.toString()). This actually means we want the value for builder.toString() BEFORE the when: block is executed. The assertion also is now very clear and readable and the intentions of the specification are very clear.

package com.mrhaki.spock 

class SampleSpec extends spock.lang.Specification {

    def "after addition of a new value the content is the initial value with the appended value"() {
        given:
        final StringBuilder builder = new StringBuilder('Spock ')

        when:
        builder << appendValue

        then:
        builder.toString() == old(builder.toString()) + appendValue

        where:
        appendValue << ['rocks!', 'is fun.', 'amazes.']
    }

}

Let's change the specification a bit so we get some failures. Instead of adding the appendValue data variable unchanged to the StringBuilder we want to add a capitalized value.

package com.mrhaki.spock 

class SampleSpec extends spock.lang.Specification {

    def "after addition of a new value the content is the initial value with the appended value"() {
        given:
        final StringBuilder builder = new StringBuilder('Spock ')

        when:
        builder << appendValue.capitalize()

        then:
        builder.toString() == old(builder.toString()) + appendValue

        where:
        appendValue << ['rocks!', 'is fun.', 'amazes.']
    }

}

If we run the specification we get assertion failures. In the following output we see such a failure and notice the value for the old() is shown correctly:

Condition not satisfied:

builder.toString() == old(builder.toString()) + appendValue
|       |          |  |                       | |
|       |          |  Spock                   | rocks!
|       |          |                          Spock rocks!
|       |          false
|       |          1 difference (91% similarity)
|       |          Spock (R)ocks!
|       |          Spock (r)ocks!
|       Spock Rocks!
Spock Rocks!

Note: If we use the old() method we might get an InternalSpockError exception when assertions fail. The error looks something like: org.spockframework.util.InternalSpockError: Missing value for expression "...". Re-ordering the assertion can help solve this. For example putting the old() method statement last. In Spock 1.0-SNAPSHOT this error doesn't occur.

For more information we can read Rob Fletcher's blog post about the old() method.

Code written with Spock 0.7-groovy-2.0.

January 25, 2011

Spocklight: Add Spock Support to Java Project in STS/Eclipse

Suppose we get assigned to an existing Java project and we want to use Spock for writing our tests (of course!). In this post we learn the steps we need to take to achieve this. We assume the Java project is built with Maven and imported into SpringSource Tool Suite (or Eclipse). For an easier way to add Spock to STS/Eclipse using Gradle we can read this blog post written by Ken Kousen.

First we start with our Java project. The project for this sample is very simple. We only have one interface and an implementation class for finding a username in a list of users. The sources for this post can be found at Github. The following pom.xml is used:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    
    <modelVersion>4.0.0</modelVersion>
    
    <groupId>com.mrhaki.app.services</groupId>
    <artifactId>user-service</artifactId>
    <packaging>jar</packaging>
    
    <name>User Service</name>
    <description>Service to work with users</description>
    
    <version>1.2</version>

</project>

This project is imported as Maven project into SpringSource Tool Suite. Now it is time to add Spock support to our project. First we change the pom.xml and add the required plugins and dependencies:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">

    <modelVersion>4.0.0</modelVersion>

    <groupId>com.mrhaki.app.services</groupId>
    <artifactId>user-service</artifactId>
    <packaging>jar</packaging>

    <name>User Service</name>
    <description>Service to work with users</description>

    <version>1.2</version>

    <properties>
        <spock.version>0.5-groovy-1.7</spock.version>
    </properties>

    <build>
        <plugins>
            <plugin>
                <groupId>org.codehaus.gmaven</groupId>
                <artifactId>gmaven-plugin</artifactId>
                <version>1.3</version>
                <executions>
                    <execution>
                        <configuration>
                            <providerSelection>1.7</providerSelection>
                        </configuration>
                        <goals>
                            <goal>compile</goal>
                            <goal>testCompile</goal>
                        </goals>
                    </execution>
                </executions>
                <dependencies>
                    <dependency>
                        <groupId>org.codehaus.gmaven.runtime</groupId>
                        <artifactId>gmaven-runtime-1.7</artifactId>
                        <version>1.3</version>
                        <exclusions>
                            <exclusion>
                                <groupId>org.codehaus.groovy</groupId>
                                <artifactId>groovy-all</artifactId>
                            </exclusion>
                        </exclusions>
                    </dependency>
                    <dependency>
                        <groupId>org.codehaus.groovy</groupId>
                        <artifactId>groovy-all</artifactId>
                        <version>1.7.5</version>
                    </dependency>
                </dependencies>
            </plugin>
            <plugin>
                <groupId>org.spockframework</groupId>
                <artifactId>spock-maven</artifactId>
                <version>${spock.version}</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>find-specs</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

    <dependencies>
        <dependency>
            <groupId>org.spockframework</groupId>
            <artifactId>spock-core</artifactId>
            <version>${spock.version}</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

</project>

Notice we must add the gmaven-plugin and spock-maven plugin to the plugins section. We must also add the spock-core dependency to our project. This is all we need to do to be able to run Spock specifications with Maven. But we need to do some extra steps to be able to run our Spock specifications from SpringSource Tool Suite.

First we add the directory src/test/groovy (which is used to save the Spock specifications in) to our project. Then we assign this new directory as source directory to the project. We open the properties of our project and select Java Build Path:

Next we click on the Add Folder... button and select the Create New Folder... button. We create the folder src/test/groovy:

We click on the Finish button and now we can select the newly created folder as a new source folder:

Now comes an important step, because we must change the Output folder of our newly created source folder. We select it and press the Edit... button. In the dialog window we select the radio button Specific output folder and fill in target/test-classes. If we don't change the output folder then STS cannot determine changes in the sources if we use the Run As | JUnit Test action.

Now we have added a new source folder to our project and assigned a correct output folder for the compiled class files:

Next we must make our Java project a Groovy project in STS. We right-click on our project and select Configure | Convert to Groovy Project:

Okay, STS is now ready for our Spock specifications! We write the following specification and save it as src/test/groovy/com/mrhaki/app/services/UserServiceImplSpec.groovy:

package com.mrhaki.app.services

import spock.lang.Specification

class UserServiceImplSpec extends Specification {
    
    UserServiceImpl userService = new UserServiceImpl();
    
    def "Search for existing name must return correct username"() {
        expect:
            userService.findUsername("Hubert A. Klein Ikkink") == 'mrhaki'
    }
    
    def "Search for non-existing name must return an empty String"() {
        expect:
            userService.findUsername('non-existing name') == ''
    }
    
    def "Search with null value for name must return an empty String"() {
        expect:
            userService.findUsername(null) == ''
    }
    
}

We can run our Spock specification with the Run or Run As | JUnit Test commands. STS recognizes the Spock specification as JUnit test and uses the testrunner to run our specification:

If the Run As command doesn't recognize the Spock specification as JUnit test then closing and reopening the source file may help.

January 21, 2011

Spocklight: Check for Exceptions with Spock

With Spock we can easily write feature methods in our specification to test if an exception is thrown by the methods invoked in a when block. Spock support exception conditions with the thrown() and notThrown() methods. We can even get a reference to the expected exception and check for example the message.

The following piece of code contains the specification to check for exceptions that can be thrown by a cook() method of the RecipeService class. And we check that exception are not thrown. The syntax is clear and concise, what we expect from Spock.

package com.mrhaki.blog

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

class RecipeServiceSpec extends Specification {
    def "If cooking for more minutes than maximum allowed by recipe throw BurnedException"() {
        setup:
            def recipeService = new RecipeService()
            def recipe = new Recipe(time: 5, device: 'oven')

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

        then:
            thrown BurnedException
    }

    def "If cooking on wrong device throw InvalidDeviceException"() {
        setup:
            def recipeService = new RecipeService()
            def recipe = new Recipe(device: 'oven', time: 10)

        when:
            recipeService.cook(recipe, 2, 'microwave')

        then:
            InvalidDeviceException ex = thrown()
            // Alternative syntax: def ex = thrown(InvalidDeviceException)
            ex.message == 'Please use oven for this recipe.'
    }

    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:
            notThrown BurnedException
            notThrown InvalidDeviceException
    }
}

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
}

class BurnedException extends RuntimeException {
    BurnedException(String message) {
        super(message)
    }
}

class InvalidDeviceException extends RuntimeException {
    InvalidDeviceException(String message) {
        super(message)
    }
}