Spock Ahoi!


Writing good tests is a hard, time consuming and confusing task.

Just take a look at the following test case:

public class UniverseTestCase extends InstrumentationTestCase {

    // Tests

    public void testThatTheSumOfTheUniverseIs42() {
        Universe universe = new Universe();
        int sum = universe.sum();
        assertThat(sum).is(42);
    }

    public void testThatTheSumOfTheUniverseIs43IfItContainsOneBlackHole() {
        Universe universe = new Universe();
        BlackHole blackHole = new BlackHole();
        universe.add(blackHole);
        int sum = universe.sum();
        assertThat(sum).is(43);
    }

    public void testThatTheSumOfTheUniverseThrowsIllegalStateExceptionIfItContainsChuckNorris() {
        Universe universe = new Universe();
        universe.add(ChuckNorris.getInstance());

        try {
            universe.sum();

            fail("Exception not thrown!");
        } catch(IllegalStateException exception) {
            // Success
        }
    }
}

Why is it bad?

Well, let’s state the obvious:

public void testThatTheSumOfTheUniverseThrowsIllegalStateExceptionIfItContainsChuckNorris() {}

This is probably not the most human readable method name you’ll ever encounter. It is decriptive yet very, and I mean very, very long and therefore hardly readable.

There are a few more things that are not optimal:

try {
    universe.sum();

    fail("Exception not thrown!");
} catch(IllegalStateException exception) {
    // Success
}

Using try and catch in your tests to ensure something is throwing an exception is not nice either.

Also, the second test looks very crowded. Spacing and some comments may fix the problem but we can do better for sure…

Well,… how?

Spock!

spock

Tell me more!

There is a testing framework called Spock. It is inspired by JUnit, jMock, RSpec, Groovy many more well-know projects. It is designed to be as descriptive and narrative as possible. So let’s see how our tests look like after the Spock face lifting.

Woop Woop!

@Title("Universe.sum()")
class UniverseSumSpecs extends AndroidSpecification {

    // Scenarios

    def "is 42 by default"() {

        given: "A new universe"
            Universe universe = new Universe()

        when: "asked for the sum"
            int sum = universe.sum()

        then: "it is 42"
            sum == 42
    }

    def "is 43 if it contains one black hole"() {

        given: "A new universe"
            Universe universe = new Universe()

        and: "a new black hole is added"
            Blackhole blackhole = new Blackhole()
            universe.add(blackhole)

        when: "asked for the sum"
            int sum = universe.sum()

        then: "it is 43"
            sum == 43
    }

    def "throws IllegalStateException if it contains Chuck Norris"() {

        given: "A new universe"
            Universe universe = new Universe()

        and: "Chuck Norris is added"
            universe.add(ChuckNorris.getInstance())

        when: "asked for the sum"
            int sum = universe.sum()

        then: "it throws an IllegalStateException"
            thrown(IllegalStateException)
    }
}

First things first:

This is not Java. This is Groovy. Why? Because Spock is a framework for Java and Groovy applications. And out of the two languages, Groovy is way cooler and way more modern than good old Java 6. We’ll see later why.

So lets examine our changes:

@Title("Universe.sum()")
class UniverseSumSpecs extends AndroidSpecification {

In Spock we write Specifactions not Test Suites. This is a well-known concept in Behavior-driven development. All our specifications have to extend the AndroidSpecification class. The @Title("") annotation describes what the specification is about. At Rheinfabrik we decided to use a string based on the class name and the method name that is under test. But basically it can be everything such as “Universe class specification that ensures the correctness of the method sum().”.

Let’s get back to the tests:

def "is 42 by default"() {

    given: "A new universe"
        Universe universe = new Universe()

    when: "asked for the sum"
        int sum = universe.sum()

    then: "it is 42"
        sum == 42
}

That’s nice!

As stated above Spock is inspired by some Behavior-driven development syntax frameworks such as RSpec. With the use of custom labels for given, when and then we can provide nice descriptions for our now Gherkin like styled tests. Note that we could read those descriptions in code and log them if we want to.

Oh and did I mention that we no longer need camelcase method names? A simple string is enough to define a method in Groovy. This is espacially handy for our last test:

def "throws IllegalStateException if it contains Chuck Norris"() {}

Let’s dive deeper into the last test:

def "throws IllegalStateException if it contains Chuck Norris"() {

    given: "A new universe"
        Universe universe = new Universe()

    and: "Chuck Norris is added"
        universe.add(ChuckNorris.getInstance())

    when: "asked for the sum"
        int sum = universe.sum()

    then: "it throws an IllegalStateException"
        thrown(IllegalStateException)
}

So instead of try and catch we can use the method thrown(). It can take a specific exception or even no argument at all if you simply want to assert that anything is thrown.

That’s it?

Of course not! This post aims to provide only a small glance over the possibilities of the Spock Framework. For more goodness take a look at the excellent documentation. There you may find features that will blow your mind! Easy Stubbing, Mocking and the amazing output of a failing test are just a few worth mentioning. I might add another post describing some of the nicest gems in Spock. Let me know if you’re interested. Meanwhile you can take a look at the tests of my MVVM sample application or Heimdall.

So how to get it?

In order to get Spock running on Android you have to use android-spock. It is fairly easy to integrate it into your existing projects. Just follow the rich documentation and you are good to go!

So remember:

Use Spock! Your tests want to be fabulous as well!