Fun With Spock & Grails – Method invocations in data tables

In this post, I show how we can keep method names in spock data tables to make testing easier.

Inspired by Rob Fletcher’s talk on Spock at the London GGUG, we continued trekking into the unknown worlds of Spock.

Very often, we write convenience methods within our services. For example, we might have a createFirePokemon() method, which uses the same call as a createWaterPokemon() method under the hood. Another example might be createAdminUser() and createRegularUser().

By using invokemethod ( line 22 ), we can provide a test that allows us to quickly run through the different permutations for a factory method. This allows us to keep method name as another parameter in the data table.

When things go wrong, the unroll annotation ( line 16 ) will show the failing test as create pokemon FIRE with method firePokemon() or create pokemon WATER with method waterPokemon(), depending on which row has failed.

The resulting spock test looks like this:

package com.pokemon

import spock.lang.*
import grails.plugin.spock.*

class PokemonServiceSpec extends UnitSpec {

   def service = new PokemonService()

   def setup() {
      mockConfig '''
        pokemon.hp = [ 100, 200, 300, 500  ]

   @Unroll("create pokemon #type with method #target()")
   def "create different Pokemons"() {
         mockDomain( Pokemon )

       when: 'create a pokemon'
         def pokemon = service.invokeMethod( target, params )

       then: 'pokemon created with no errors and of the right type'
         pokemon.type == type

         target             | type                  | params
         'firePokemon'      | PokemonType.FIRE      | [ name: 'Charzard' ]
         'waterPokemon'     | PokemonType.WATER     | [ name: 'Squirtle' ]
         'electricPokemon'  | PokemonType.ELECTRIC  | [ name: 'Pikachu' ]
         'psychicPokemon'   | PokemonType.PSYCHIC   | [ name: 'MewTwo' ]


6 thoughts on “Fun With Spock & Grails – Method invocations in data tables

    1. Peter Ledbrook

      service.”$target”(*params) might be more reliable, i.e. probably best to use the spread operator on the arguments.

      1. Tomas Lin Post author

        When I try to use the spread operator in this example, get this error:

        cannot spread the type java.util.LinkedHashMap with value { name = ‘Pikachu’ }

        works with Rob’s suggestion.

  1. Rob Fletcher

    Spread would work if you were passing a list of params. The cool thing then would be that each invocation would not necessarily have to have the same method signature.

    1. Peter Ledbrook

      Ah sorry, didn’t notice that ‘params’ was a map. Yes, spread operator only works on lists/arrays.


Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s