Testing Closures in Groovy

Post by Scott Vlaminck

I use a lot of closures in Groovy. This often makes my code more expressive and readable – and easier to write. But testing what the closure is doing can sometimes be difficult if the closure isn’t something trivial.

To deal with this, I’ve been using a simple class that I created to capture the calls that are made by a closure. I set this class as the closure’s delegate and then execute the closure. This delegate just has an invokeMethod() implementation that tracks all method calls made by the closure and the delegate will also execute any nested closures as necessary:

def invokeMethod(String name, args)
{
  // track the call
  def call = [name:name, args:args]
  current.calls < < call

  // handle sub calls, if applicable
  if(args && args[-1] instanceof Closure)
  {
    // ... execute the nested closure
  }
}

The best way to describe how to use it is to show it in action.

def c = {
  hello('world')
  one('1') {
    two(2)
  }
}

def capture = new ClosureCapture()
c.delegate = capture
c.call()

assert "hello" == capture.calls[0].name
assert "world" == capture.calls[0].args[0]

assert "one" == capture.calls[1].name
assert "1" == capture.calls[1].args[0]

assert "two" == capture.calls[1].calls[0].name
assert 2 == capture.calls[1].calls[0].args[0]

Over time, I’ve also added the ability to optionally continue execution when an exception is encountered and a few other things. Here’s the full implementation of the class:

class ClosureCapture
{
  def stopOnException = true

  private hasException = false
  private root = [calls:[]]
  private current = root

  def getCalls()
  {
    return root.calls
  }

  def invokeMethod(String name, args)
  {
    // track call
    def call = [name:name, args:args, parent:current, calls:[], throwable:null]
    current.calls < < call

    // handle sub calls, if applicable
    if(args && args[-1] instanceof Closure)
    {
      def previousCall = current
      current = call

      def c = args[-1]

      def previousDelegate = c.delegate
      c.delegate = this

      try
      {
        c.call()
      }
      catch(Throwable t)
      {
        call.throwable = t
        hasException = true

        if(stopOnException)
        {
          throw t
        }
      }
      finally
      {
        // undo the delegate
        c.delegate = previousDelegate
      }

      // but do not undo the current call on exception,
      // ... so that we can be interrogated about which call failed
      current = previousCall
    }

  }
}

I’ve found this to be really useful in testing. How do you test closure implementations?

About Scott Vlaminck

believes software development is more about people than technology; believes in agile processes; software developer, engineer, designer, architect, or whatever they're calling us these days; enjoys discussing software design; working on a program to write other programs (but it hasn't written itself yet).
This entry was posted in Software Development and tagged , , , . Bookmark the permalink.

Related Posts:

5 Responses to Testing Closures in Groovy

  1. Mockito. It has a lot of the spying features we wanted so long ago but without all the record/playback junk.

  2. Pingback: Tweets that mention refactr blog on software development, design, agile processes, and business Blog Archive ยป Testing Closures in Groovy -- Topsy.com

  3. Rob Fletcher says:

    Can you give a real world example of where this is useful? Tests like this show at a microscopic level that execution proceeds along a certain path and that certain expectations are met. What is often missing is any proof that the expectations represent reality in any meaningful way. It’s easy to prove that your Closure makes certain calls on its delegate but there’s nothing to say there isn’t a glaring flaw in your understanding of the effect of those calls. For example, this technique could be used to test a Grails criteria query without in any way validating that the query is actually going to produce the right results in reality.

  4. Rob, you have a good point. Testing the closure for the exact method calls that are made can be brittle and not actually test what you want. This class is intended as just another tool for testing, not the only solution.

    As an example, I have custom xml encoders for some of my domain classes. The class discussed above makes some of my tests more readable when verifying the ordering of related objects in the xml output (e.g. a “hasMany” relationship). However, I have other tests of the same encoder that verify the actual xml output. It comes down to which approach is more readable, while also keeping in mind how brittle the test is.

    I’ve also used this class to verify that in specific cases, no methods are called at all by a closure. For a simple example, using the Commentable plugin requires an onAddComment closure to exist for commentable domains. I want to be sure that the attribute exists, but that it does nothing. This approach allows me to verify that as well.

  5. Pingback: An Army of Solipsists » Blog Archive » This Week in Grails

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>