Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Cleaner Tests With Spock

DZone's Guide to

Cleaner Tests With Spock

Good coding practices apply to tests in the same ways they apply to production code. See how Spock can make your tests cleaner and easier to work with.

· Java Zone
Free Resource

Try Okta to add social login, MFA, and OpenID Connect support to your Java app in minutes. Create a free developer account today and never build auth again.

One obvious fact about tests that is often overlooked is: Tests are code. This implies that the tests, as any kind of code, have to be read, understood, and maintained. We’re smart, educated people, familiar with all the craftsmanship practices of Clean Code, so we keep our tests clean. And yet, if you’re like me, you might be dissatisfied with the way some of your tests look. Not so long ago, I found my way to resolve this problem – the Spock framework.

Let’s Groove

Spock tests are written in Groovy – a concise JVM language with a lot of syntactic sugar. Groovy has quite a few features that make it a perfect fit for testing:

  • Quoted identifiers – after enclosing an identifier in quotes, it can contain illegal characters like spaces or keywords:
    def "naming my tests like a boss"() {
        ...
    }
  • Type inference – you can skip types when declaring variables, constants, and so on – def or static final is enough:
    static final LOOK_MAN = "no need to declare types!"
    
    def aintThat = "awesome?"
  • Collection literals – you can declare collections using a friendly syntax. No more Arrays.asList() and others alike:
    def list = ["tidy","java","is","the","best"]
    
    def map = ["best blog": "tidyjava.com", "best blog aggregator": "dzone.com"]
  • And more! I could keep going with this, but we’re not here (only) to praise Groovy.

Hello, Spock!

Test classes in Spock are called specifications, and they have to extend the Specification class. A single test is called a feature method and is divided into blocks following a BDD given-when-then style. Code speaks louder than words, so take a look at the following example:

class GreeterSpec extends Specification {

    def "should say hello"() {
        given:
        def greeter = new Greeter()

        when:
        def result = greeter.sayHello()

        then:
        result == "hello!"
    }
}

In the then block, you put expressions that evaluate to a boolean, each in a new line. For simple cases, like the one above, we can combine when and then into an expect block:

class GreeterSpec extends Specification {

    def "should say hello"() {
        given:
        def greeter = new Greeter()

        expect:
        greeter.sayHello() == "hello!"
    }
}

As you can see, the feature methods benefit from Groovy’s quoted identifiers. Another nice thing is that they don’t require any special annotations.

Block Descriptions

To further align with the BDD style, Spock allows you to specify descriptions for each block, by simply putting a String after block’s declaration:

class GreeterSpec extends Specification {

    def "should present block descriptions"() {
        given: "you're a smart guy"
        def greeter = new Greeter()

        when: "I present you block descriptions"
        def result = greeter.sayHello()

        then: "you'll see the potential to make your tests better"
        result == "hello!"
    }
}

Data-Driven Tests

Spock really shines out when it comes to data-driven tests, also known as parameterized tests (in the JUnit world). Instead of creating multiple similar tests, custom assertion methods, or returning ugly arrays of Objects, you simply put your data on a table or a pipe:

def "should present tables as a great feature"() {
    given:
    def b = new Blog(name)

    expect:
    b.isTheBest() == best

    where:
    name        | best
    "Tidy Java" | true
    "Yegor256"  | false
    "Hehehehe"  | false
}
def "should present data pipes as a great feature"() {
    given:
    def b = new Blog(name)

    expect:
    b.isFunToRead()

    where:
    name << ["Tidy Java", "Yegor256", "Hehehehe"]
}

Clean Mocking

Spock ships with built-in mocking support. Recording and verifying a mock look (arguably) cleaner than in frameworks like Mockito:

def object = Mock(MyClass)

def "should present recording a mock"() {
    given:
    object.method(argument) >> "result for given argument here"
    object.method({ it.startsWith("WOW") }) >> "closure magic allowed"
    object.method(_) >> "underscore works as a wildcard"

    // stuff
}

def "should present verifying a mock"() {
    // stuff

    then:
    1 * object.method(argument)
    1 * object.method({ it == "closure magic works here too!" })
    1 * object.method(_)
}

Great Failure Messages

When a test fails, it’s sometimes hard to say why exactly did it fail. With Spock, this is much less of a problem than in JUnit. Just take a look at those cool failure messages:

Condition not satisfied:

object.method() == expectedResult
|         |      |  |
|         2      |  3
|                false
["you get the result", "of object's toString() here"]

Expected :3

Actual   :2

And for mocks:

Too few invocations for:

1 * object.method("goodArgument")   (0 invocations)

Unmatched invocations (ordered by similarity):

1 * object.method('badArgument')

There’s More!

I don’t mean to rewrite or compete with Spock’s documentation, so we’ll end up here. Read the docs or just play with the framework if you wish to learn more. I hope that at least some of you are now convinced that it’s a great tool to write great tests.

Build and launch faster with Okta’s user management API. Register today for the free forever developer edition!

Topics:
spock ,clean tests ,java ,groovy ,tutorial

Published at DZone with permission of Grzegorz Ziemoński, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}