Spock (the specification framework)

by Matt Cholick

The Spock framework is a recent discovery that I can't stop raving about. It's nice that in the Java community we seem to have a surfeit of frameworks that support testing in some way: JUnit, TestNG, Mockito, JMock, EasyMock, and DbUnit are all tools that I've heard about at some point. Spock is different in one key way: the other frameworks are there to support testing and mocking, but Spock takes an additional step and also elevates the test itself to a first class artifact.

Consider the following example test:

@Slf4j
class RatingServiceIntSpec extends BaseDatabaseIntSpec {

    RatingService ratingService

    def setup() {
        ratingService = injector.getInstance(RatingService.class)
    }

    def 'Test that save ratings persists and update'() {
        given: 'Database starts empty of ratings'
        assert 0 == em.createQuery('select count(*) from Rating').singleResult
        scaffoldUsers()
        scaffoldMovies()

        when: '2 ratings are saved'
        ratingService.saveRatings([
                new Rating([ratingId: new RatingId([user: user1, movie: movie1]), positive: true]),
                new Rating([ratingId: new RatingId([user: user1, movie: movie2]), positive: false])
        ])

        then: 'The database contains 2 ratings'
        2 == em.createQuery('select count(*) from Rating').singleResult

        when: 'A rating is updated'
        ratingService.saveRatings(
                [new Rating([ratingId: new RatingId([user: user1, movie: movie1]), positive: false])]
        )
        Rating rating = em.find(Rating.class, new RatingId([user: user1, movie: movie1]))

        then: 'The database still only contains 2 ratings'
        2 == em.createQuery('select count(*) from Rating').singleResult

        and: 'The update is reflected'
        null != rating.positive
        !rating.positive
    }
}

When stripped down to just the labels and descriptions, this test becomes:

Test that save ratings persists and update. Given database starts empty of ratings, when 2 ratings are saved, then the database contains 2 ratings. When a rating is updated, then the database still only contains 2 ratings and the update is reflected.

This is a readable paragraph describing how the class under test should behave. Brilliant. One frustration I sometimes feel when writing tests is that they can become impenetrable to another developer, sometimes even more so than code. This is a problem because I feel that a great test should describe the software and my intent in addition to exercising code. Spock helps this process along.

The great part is that you can pretty much learn everything about Spock in an hour. It's simple, but the structure it adds to tests really transforms them into artifacts that hold documentation value independent of actual test coverage.