Unit testing Grails controllers, revisited - No Fluff Just Stuff

Unit testing Grails controllers, revisited

Posted by: Kenneth Kousen on November 10, 2010

I’ve been neglecting my blog, which I blame on a combination of using twitter and being on a book project.  More about those later.  In the meantime, I’ve recently been working on some Grails projects and found an issue with unit testing controllers.

I’m now on Grails 1.3.5, and I’m trying hard to do unit tests rather than integration tests.  I rapidly hit a couple of issues, which I want to log here mostly so I don’t forget how I resolved them.

Let’s look at a trivial Hello, World type of example.  Say I have a Grails project called helloworld, with a single controller.

package cc.hello
class WelcomeController {
    def index = {
        log.info "params: $params"

        String name = params.name ?: 'Grails'
        render "Hello, $name!"
    }

    def redirectMethod = { redirect action:"other" }

    def other = { render "Made it here" }
}

This is (deliberately) very similar to the example in the Grails reference guide. If I specify a parameter called name, the index action returns “Hello, $name”, otherwise it returns “Hello, Grails!”. I also added a method to do a redirect, because the generated Grails controllers do that a lot and I want to be able to test them.

To keep the story short, here’s my unit test, which I’ll explain afterwards.

package cc.hello

import grails.test.*

class WelcomeControllerTests extends ControllerUnitTestCase {
    WelcomeController controller

    protected void setUp() {
        super.setUp()
        controller = new WelcomeController()
        mockController(WelcomeController)
    }

    void testIndexNoParameters() {
        controller.index()
        assertEquals "Hello, Grails!", controller.response.contentAsString
    }

    void testIndexWithName() {
        controller.params.name = "Dolly"
        //mockParams.name = "Dolly"
        controller.index()
        assertEquals "Hello, Dolly!", controller.response.contentAsString
    }

    void testRedirect() {
        controller.redirectMethod()
        assertEquals "other", controller.redirectArgs['action']
    }
}

The key features of this test are:

  • I have to call super.setUp() in my setUp() method, or calling mockController throws a NullPointerException. The actual error is “Cannot invoke containsKey() on a null object”. In other words, the maps for the mock objects aren’t being set up without calling setUp() in the superclass. This cost me a lot of searching to figure out.
  • Unlike mocking the domain objects, you can instantiate the controller and then call MockController afterwards. For domain classes you either have to mock the domain first, or call the version of mockDomain or mockForConstraintsTests that takes a list of instances as its second argument.
  • The reference documentation tests the redirected method by comparing the generated URL to controller.response.redirectedUrl. That expression always returned null for me, however, and a search of the grails-users list showed it returns null for lots of people. Eventually I found in the excellent Grails in Action book by Peter Ledbrook and Glen Smith that the redirectArgs map has the name of the redirected action under the "action" key, so I can just compare to that. That’s what I’m doing above.

Hopefully these points will help keep someone else from making the same mistakes I did.


Kenneth Kousen

About Kenneth Kousen

Ken Kousen is a Java Champion, several time JavaOne Rock Star, and a Grails Rock Star. He is the author of the Pragmatic Library books “Mockito Made Clear” and “Help Your Boss Help You,” the O'Reilly books “Kotlin Cookbook”, “Modern Java Recipes”, and “Gradle Recipes for Android”, and the Manning book “Making Java Groovy”. He also has recorded over a dozen video courses for the O'Reilly Learning Platform, covering topics related to Android, Spring, Java, Groovy, Grails, and Gradle.

His academic background include BS degrees in Mechanical Engineering and Mathematics from M.I.T., an MA and Ph.D. in Aerospace Engineering from Princeton, and an MS in Computer Science from R.P.I. He is currently President of Kousen IT, Inc., based in Connecticut.

Why Attend the NFJS Tour?

  • » Cutting-Edge Technologies
  • » Agile Practices
  • » Peer Exchange

Current Topics:

  • Languages on the JVM: Scala, Groovy, Clojure
  • Enterprise Java
  • Core Java, Java 8
  • Agility
  • Testing: Geb, Spock, Easyb
  • REST
  • NoSQL: MongoDB, Cassandra
  • Hadoop
  • Spring 4
  • Cloud
  • Automation Tools: Gradle, Git, Jenkins, Sonar
  • HTML5, CSS3, AngularJS, jQuery, Usability
  • Mobile Apps - iPhone and Android
  • More...
Learn More »