act:ualise | technology

23 Jul, 2009

JSON Webtests with Grails

Posted by: j pimmel In: development|functional tests|grails

I recently figured out how to use WebTest for functional testing of Grails controller actions that render JSON. That said, I’m not convinced it’s the best way – I’m fairly sure the gFunc plugin would do it nicely, though I ran into problems with it clean compiling the whole app on every run.

Custom steps

It’s been possible to add custom steps to Webtests for some time. Assuming you have v0.6 of the plugin (or you use Grails 1.1), then this writeup provides some useful background and also a ‘Hello, World’ type example.

In an ideal world

On the surface it seems that we could therefore have a jsonVerify step which is quite simply:

class JsonVerifyStep extends Step {

    String expected

    void doExecute() {
        def jsonServed = context.currentResponse.inputStream as JSON
        def jsonExpected = expected as JSON
        assert jsonExpected, jsonServed
    }

}

Annoyingly, it’s not this easy.

Grails, Webtest and (sigh) Classpath’s

Webtest is spawned by a forked Ant process (see ${pluginDir}/webtest-n.n/scripts/call-webtest.xml) which means you get a  limited classpath due to JAR version conflicts.

So it’s not possible to add the Grails classpath (or even just $GRAILS_HOME/dist/grails-web-n.n.jar) which contains all the handy JSON library code that we’re so accustomed to when rendering JSON responses.

Solution (has some camembert)

My “solution” was to add two jars (json-lib & ezmorph) to your Grails lib and also tweak the call-webtest.xml file with the following change:

<fileset dir="${grailsHome}/lib" includes="commons-cli*.jar,commons-beanutils*.jar"/>

With the resulting custom step you can test your JSON response

import com.canoo.webtest.steps.Step
import net.sf.json.JSON
import net.sf.json.groovy.GJson
import org.apache.commons.io.IOUtils
import org.apache.log4j.Logger
import net.sf.json.test.JSONAssert

class JsonVerifyStep extends Step {
    private static Logger log = Logger.getLogger(JsonVerifyStep)

    String expected

    void doExecute() {
        GJson.enhanceClasses() // neccessary for the net.sf.JSON stuff in Groovy
        def jsonServed = IOUtils.toString(context.currentResponse.inputStream) as JSON // wants a string
        def jsonExpected = expected as JSON
        JSONAssert.assertEquals jsonExpected, jsonServed
    }
}

And you would implement your webtest as follows:

    def testSomeJSONResponse() {
        webtest('Example JSON webtest') {
            invoke('/controller/actionJSON')
            jsonVerify(expected: '{"totalRecords":2,"results":[{"id":16,"year":2009,"name":"ZZ Top"},{"id":2,"year":2009,"name":"Aerosmith"}')
        }

    }

5 Responses to "JSON Webtests with Grails"

1 | fafaNo Gravatar

July 24th, 2009 at 6:37 am

Avatar

Thank you! I’ll try and see how that works…

2 | James LorenzenNo Gravatar

July 24th, 2009 at 11:43 am

Avatar

Our team uses REST services exclusively using jersey (wish it was grails) with extjs on the frontend.
I have written about how we write tests for our REST Services here using groovy and HttpBuilder.
http://jlorenzen.blogspot.com/2009/01/testing-rest-services-with-groovy.html

3 | j pimmelNo Gravatar

July 24th, 2009 at 12:15 pm

Avatar

James,

Thanks for your comment; I had in fact come across your posting and I liked it’s brevity/simplicity.

Using Webtest allows for the automation by Grails of bringing up the application and then running our functional tests against it, whereas Units by themselves don’t do that.

That said, this experiment came as a consequence of a problem I ran into writing a controller action test. I’ll try to document the problem I had if I can’t solve it on a revisit.

4 | Glenn SaquiNo Gravatar

July 27th, 2009 at 1:21 am

Avatar

Jerome,

I tried this and it seemed to work pretty nicely with gfunct
def array = new JSONArray(“[${response.contentAsString}]“)

notice how I’ve had to add [ and ] at the start and end of the string. Not sure why it’s needed but hey at least you then can work with the json array as you need it.

5 | Rob FletcherNo Gravatar

August 19th, 2009 at 8:44 am

Avatar

Yeah, that works fine in the functional-test plugin. The trick is when you want to PUT or POST JSON data back to the server!

Comment Form

About

act:ualise | technical blog

notes, observations and obssessions on software quality, agile software development, testing and grails


part of the EnergizedWork experiment