Hamcrest matchers in Spock

Working with Spock we forget about a hidden gem – Hamcrest matchers. They are assertions turned into method objects. A matcher is usually parametrized with an expected value and is executed latel on the actual one.

When can we use it? For example in a table-driven specification with expected values in the table.

An expected value can be one of the following:

  • exact value
  • ‘not available’ text
  • any number

How can we express these assertions without Hamcrest?

then:
if (exactValue) {
    assert field == exactValue
} else if (notAvailable) {
    assert field == messageSource.getMessage('na', null, LocaleContextHolder.locale)
} else if (isNumber) {
    assert field.number
} else {
    assert false, 'Wrong test configuration'
}

where:
exactValue | notAvailable | isNumber
'12345'    | null         | null
null       | true         | null
null       | null         | true

Bufff…

  • there is a lot of boilerplate code
  • the specification is misleading – what does it mean that expected exactValue is null? Is it really null? Or simply this condition should not be checked?

How can we refactor this code using Hamcrest matchers?

then:
that field, verifiedBy

where:
verifiedBy << [
    org.hamcrest.CoreMatchers.equalTo('12345')
    notAvailable(),
    isNumber(),
]

...

def notAvailable() {
    org.hamcrest.CoreMatchers.equalTo messageSource.getMessage('na', null, LocaleContextHolder.locale)
}

def isNumber() {
    [
        matches: { actual -> actual.number },
        describeTo: { Description description -> 'should be a number' }
    ] as BaseMatcher
}

Result – shorter and more expressive feature method code.

You can find the full source code with mocked messageSource in this gist. For more information about Hamcrest matchers, check Luke Daley’s article.

Spock Grails extension – execute or ignore specification depending on the environment

My newest Spock Grails environment extensions plugin adds two annotations to Spock:

  • @ExecuteForEnvironment
  • @IgnoreForEnvironment

You specify environment(s) as the annotation parameter, e.g. @ExecuteForEnvironment(['test_integration', 'test_performance']) or @IgnoreForEnvironment('test'). More examples can be found in the official user guide.

grails-equals-hashcode plugin – lazy property evaluation

I recently relesead a new 0.2.0 version of Grails equals hashCode plugin.

There two mayor changed:

  • the package of EqualsHashCodeSpec changed from es.osoco to es.osoco.grails.plugins according to Osoco‘s convention (update your imports after upgrading)
  • modified and ignored property values can be evaluated lazily. You find an example in the user guide

Hope you find the plugin useful!