Search

Dark theme | Light theme

May 14, 2013

Spocklight: Using a Custom Hamcrest Matcher

In a previous blog post we learned how we can use Hamcrest matchers. We can also create a custom matcher and use it in our Spock specification. Let's create a custom matcher that will check if elements in a list are part of a range.

In the following specification we create the method inRange() which will return an instance of a Matcher object. This object must implement a matches() method and extra methods to format the description when the matcher fails. We use Groovy's support to create a Map and turn it into an instance of BaseMatcher.

// File: SampleSpecification.groovy
package com.mrhaki.spock

@Grab('org.hamcrest:hamcrest-all:1.3')
import static org.hamcrest.Matchers.*
import org.hamcrest.*

@Grab('org.spockframework:spock-core:0.7-groovy-2.0')
import static spock.util.matcher.HamcrestSupport.*
import spock.lang.Specification

class SampleSpecification extends Specification {

    def "sample usage of custom hamcrest matcher"() {
        given:
        final list = [2,3,4]

        expect:
        that list, inRange(0..10)
        that list, not(inRange(0..3))
    }

    /**
     * Create custom Hamcrest matcher to check if a list has elements
     * that are contained in the define range.
     *
     * @param range Range to check if elements in the list are in this range.
     * @return Hamcrest matcher to check if elements in the list are part of the range.
     */
    private inRange(final range) {
        [
            matches: { list -> range.intersect(list) == list },
            describeTo: { description ->
                description.appendText("list be in range ${range}")
            },
            describeMismatch: { list, description ->
                description.appendValue(list.toListString()).appendText(" was not in range ${range}")
            }
        ] as BaseMatcher
    }

}

We can run the specification ($ groovy SampleSpecification.groovy) and everything should work and all tests must pass.

We change the code to see the description we have added. So we change that list, inRange(0..10) to that list, inRange(0..3). We run the specification again ($ groovy SampleSpecification.groovy) and look at the output:

JUnit 4 Runner, Tests: 1, Failures: 1, Time: 200
Test Failure: sample usage of custom hamcrest matcher(com.mrhaki.spock.SampleSpecification)
Condition not satisfied:

that list, inRange(0..3)
|    |
|    [2, 3, 4]
false

Expected: list be in range [0, 1, 2, 3]
     but: "[2, 3, 4]" was not in range [0, 1, 2, 3]

    at com.mrhaki.spock.SampleSpecification.sample usage of custom hamcrest matcher(SampleSpecification.groovy:18)

Notice the output shows the text we have defined in the describeTo() and describeMismatch() methods.

Code written with Spock 0.7-groovy-0.2.