Integration tests of controllers in Grails

The documentation on doing integration tests of controllers is a bit thin. I had to ask on the mailing list about how to do some of the basics, so I thought I’d make a record of the results here.

First of all, Grails distinguishes between unit tests and integration tests, in that integration tests involve functionality from the rest of the Grails environment. Running an integration test is rather like using the Grails console. All the dynamic finders are available, as well as any other properties injected by Grails. In a unit test, all of those items would have to be mocked somehow in order to isolate the class under test.

Chapter 6 of DGG discusses testing. The unit testing section is still pretty good, and there’s a section on using Mocks and Stubs which is interesting but feels a bit dated. I’m really looking forward to seeing the next edition of the book. Rumor has it that that’s already in the works.

The cool part is that according to the wiki, when you run “grails test-app”, Grails automatically injects a MockHttpServletRequest, MockHttpServletResponse, and a MockHttpSession from Spring into each integration test. The details of those classes are found in the API documentation for Spring in the org.springframework.mock.web package.

The example given in the wiki page referenced above shows what to do if controller methods end with either a render or a redirect. It turns out that the test case can use the mocked response property to check the resulting value. The sample there looks like


class FooController {
    def text = { render "bar" }

    def someRedirect = { redirect(action:"bar") }
}

so that the test case is


class FooControllerTests extends GroovyTestCase {
    void testText() {
        def fc = new FooController()
        fc.text()
        assertEquals "bar", fc.response.contentAsString
    }

    void testSomeRedirect() {
        def fc = new FooController()
        fc.someRedirect
        assertEquals "/foo/bar", fc.response.redirectedUrl
    }
}

Those are both interesting, of course, and it’s good that they point to the getContentAsString() and getRedirectedUrl() methods in the MockHttpServletResponse classes. My problem is that since both controller methods are invoked without input parameters, neither example shows how to set them.

Here’s the situation I was trying to test, reduced to trivial form. I have a Message class:


class Message {
    String sender
    String msg
}

The generated controller has (among other things) a create action that looks like


class MessageController {
    // ... lots of other actions ...
    def create = {
        def message = new Message()
        message.properties = params
        return ['message':message]
    }
}

So to test the create action, I need to send in some values in the params map.

To make a long story short (too late, I know, but so be it), there are two ways to do it. Here’s the first one:


class MessageControllerTests extends GroovyTestCase {
    void setUp() {
        new Message(sender:'me',msg:'test').save()
    }

    void testCreate() {
        def m = Message.findBySender('me')
        def mc = new MessageController()
        mc.request.parameters = [sender:'me',msg:'test']
        def message = mc.create().message
        assertToString message, "$m"
    }

    void tearDown() {
        Message.findBySender('me').delete()
    }
}

I needed to create a new message in setUp and save it, so that it would be accessible in my test. At the end I disposed of the message in the tearDown method.

In testCreate, I set the request parameters by invoking the setParameters(Map) method of MockHttpServletRequest. That took me a while to realize — I kept trying to set a “params” variable, since that’s what exists in the controller itself. Of course, from the request point of view, the map is a collection of parameters, not params.

The other approach is to deal with the params map directly by replacing the mc.request.parameters line above with


mc.params.sender = 'me'
mc.params.msg = 'test'

That works too and does essentially the same thing. I think I prefer the setParameters version, because I can see exactly what method is being invoked that way. Still, the difference is probably just a question of style.

(My thanks to Chris Chen, ckchris@idream.net, and Burt Beckwith, burt@burtbeckwith.com, for answering my question on the Grails user’s list.)

Finally, back in March of 2007, Glen Smith (of course) wrote up a nice blog post on unit testing controllers rather than doing integration testing. That relies on mocking the request, response, session, etc. with closures. While it demonstrates an interesting usage of metaprogramming to handle all the mocks, it feels like a lot of overhead to put in for each controller. Graeme himself commented on that and suggested developing a withMockController method for GroovyTestCase that did all the overhead for you. I don’t know if that ever actually happened or not, but it looks promising.

Since the whole Grails environment doesn’t have to be bootstrapped, unit testing with those mocks is inevitably going to be faster.

At the moment, I’m quite content to do integration tests, because I’m partly doing the tests to see how the environment works.

I sometimes feel that writing tests is a lot like brushing your teeth. Dentists will tell you that there are better ways and worse ways to brush, but in all honesty, if you just do it at all you’re way ahead of the game. I’m going to try and follow that philosophy and not worry about doing tests perfectly as long as I’m doing them at all.

About Ken Kousen
I teach software development training courses. I specialize in all areas of Java and XML, from EJB3 to web services to open source projects like Spring, Hibernate, Groovy, and Grails. Find me on Google+ I am the author of "Making Java Groovy", a Java / Groovy integration book published by Manning in the Fall of 2013, and "Gradle Recipes for Android", published by O'Reilly in 2015.

9 Responses to Integration tests of controllers in Grails

  1. chriskwiatkowski says:

    I was trying to find information on testing controllers and your blog post was most helpful.
    Thanks for posting!

  2. reza says:

    it was helpful. thx

  3. Flo says:

    This is probably obvious to you, but for some people new to all this (including myself 😉 ): if you try to recreate the above example, add

    String toString() { “sender:$sender message:$msg” }

    or something similar to your domainclass. The standard toString method returns the ID and subsequently, the assert will fail (as “1” != “2”)

  4. Vijay Kumar says:

    Thank you, this is a great post. I didn’t know how to access the request params from the test method.

    Thanks again.
    Vijay

  5. Amit Jain says:

    Many thanks for posting this!

  6. Caleb says:

    Thanks for posting this. I realize this post is over two years old, so you might know this already, but it seems there is a difference between using controller.params and controller.request.parameters. When I did this, using Grails 1.2.4:


    def controller = new ExampleController()
    controller.params['collection.0.name'] = "Test"
    controller.action()

    I expected, in my controller’s action(), for params[‘collection’] == null to be false. Instead, it was true, which interfered with my test. Using this, instead:


    controller.request.parameters = ['collection.0.name': "Test"]

    worked as expected.

  7. Pingback: Grails Unit Testing investigation : be2 SCRUM blog

  8. Chandan Luthra says:

    Thanks for posting such a descriptive way for testing controllers.

  9. manoj says:

    yaa its good

Leave a Reply

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

WordPress.com Logo

You are commenting using your WordPress.com 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 )

Google+ photo

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

Connecting to %s

%d bloggers like this: