(Quick Reference)

9.1.1 Unit Testing Controllers - Reference Documentation

Authors: Graeme Rocher, Peter Ledbrook, Marc Palmer, Jeff Brown, Luke Daley, Burt Beckwith

Version: null

9.1.1 Unit Testing Controllers

The Basics

You use the grails.test.mixin.TestFor annotation to unit test controllers. Using TestFor in this manner activates the grails.test.mixin.web.ControllerUnitTestMixin and its associated API. For example:

import grails.test.mixin.TestFor

@TestFor(SimpleController) class SimpleControllerTests { void testSomething() {

} }

Adding the TestFor annotation to a controller causes a new controller field to be automatically created for the controller under test.

The TestFor annotation will also automatically annotate any public methods starting with "test" with JUnit 4's @Test annotation. If any of your test method don't start with "test" just add this manually

To test the simplest "Hello World"-style example you can do the following:

// Test class
class SimpleController {
    def hello() {
        render "hello"
    }
}

void testHello() {
    controller.hello()

assert response.text == 'hello' }

The response object is an instance of org.codehaus.groovy.grails.plugins.testing.GrailsMockHttpServletResponse which extends Spring's org.springframework.mock.web.MockHttpServletResponse and has a number of useful methods for inspecting the state of the response.

For example to test a redirect you can use the redirectUrl property:

// Test class
class SimpleController {
    def index() {
        redirect action: 'hello'
    }
    …
}

void testIndex() {
    controller.index()

assert response.redirectedUrl == '/simple/hello' }

Testing View Rendering

To test view rendering you can inspect the state of the controller's modelAndView property (an instance of org.springframework.web.servlet.ModelAndView) or you can use the view and model properties provided by the mixin:

// Test class
class SimpleController {
    def home() {
        render view: "homePage", model: [title: "Hello World"]
    }
    …
}

void testIndex() {
    controller.home()

assert view == "/simple/homePage" assert model.title == "Hello World" }

Testing Template Rendering

Unlike view rendering, template rendering will actually attempt to write the template directly to the response rather than returning a ModelAndView hence it requires a different approach to testing.

Consider the following controller action:

class SimpleController {
    def display() {
        render template:"snippet"
    }
}

In this example the controller will look for a template in grails-app/views/simple/_snippet.gsp. You can test this as follows:

void testDisplay() {
    controller.display()
    assert response.text == 'contents of template'
}

However, you may not want to render the real template, but just test that is was rendered. In this case you can provide mock Groovy Pages:

void testDisplay() {
    views['/simple/_snippet.gsp'] = 'mock contents'
    controller.display()
    assert response.text == 'mock contents'
}

Testing XML and JSON Responses

XML and JSON response are also written directly to the response. Grails' mocking capabilities provide some conveniences for testing XML and JSON response. For example consider the following action:

def renderXml() {
    render(contentType:"text/xml") {
        book(title:"Great")
    }
}

This can be tested using the xml property of the response:

void testRenderXml() {
    controller.renderXml()
    assert "<book title='Great'/>" == response.text
    assert "Great" == response.xml.@title.text()
}

The xml property is a parsed result from Groovy's XmlSlurper class which is very convenient for parsing XML.

Testing JSON responses is pretty similar, instead you use the json property:

// controller action
def renderJson() {
    render(contentType:"text/json") {
        book = "Great"
    }
}

// test
void testRenderJson() {

controller.renderJson()

assert '{"book":"Great"}' == response.text assert "Great" == response.json.book }

The json property is an instance of org.codehaus.groovy.grails.web.json.JSONElement which is a map-like structure that is useful for parsing JSON responses.

Testing XML and JSON Requests

Grails provides various convenient ways to automatically parse incoming XML and JSON packets. For example you can bind incoming JSON or XML requests using Grails' data binding:

def consumeBook() {
    def b = new Book(params['book'])

render b.title }

To test this Grails provides an easy way to specify an XML or JSON packet via the xml or json properties. For example the above action can be tested by specifying a String containing the XML:

void testConsumeBookXml() {
    request.xml = '<book><title>The Shining</title></book>'
    controller.consumeBook()

assert response.text == 'The Shining' }

Or alternatively a domain instance can be specified and it will be auto-converted into the appropriate XML request:

void testConsumeBookXml() {
    request.xml = new Book(title:"The Shining")
    controller.consumeBook()

assert response.text == 'The Shining' }

The same can be done for JSON requests:

void testConsumeBookJson() {
    request.json = new Book(title:"The Shining")
    controller.consumeBook()

assert response.text == 'The Shining' }

If you prefer not to use Grails' data binding but instead manually parse the incoming XML or JSON that can be tested too. For example consider the controller action below:

def consume() {
    request.withFormat {
        xml {
            render request.XML.@title
        }
        json {
            render request.JSON.title
        }
    }
}

To test the XML request you can specify the XML as a string:

void testConsumeXml() {
    request.xml = '<book title="The Stand" />'

controller.consume()

assert response.text == 'The Stand' }

And, of course, the same can be done for JSON:

void testConsumeJson() {
    request.json = '{title:"The Stand"}'
    controller.consume()

assert response.text == 'The Stand' }

Testing Spring Beans

When using TestFor only a subset of the Spring beans available to a running Grails application are available. If you wish to make additional beans available you can do so with the defineBeans method of GrailsUnitTestMixin:

class SimpleController {
    SimpleService simpleService
    def hello() {
        render simpleService.sayHello()
    }
}

void testBeanWiring() {
    defineBeans {
        simpleService(SimpleService)
    }

controller.hello()

assert response.text == "Hello World" }

The controller is auto-wired by Spring just like in a running Grails application. Autowiring even occurs if you instantiate subsequent instances of the controller:

void testAutowiringViaNew() {
    defineBeans {
        simpleService(SimpleService)
    }

def controller1 = new SimpleController() def controller2 = new SimpleController()

assert controller1.simpleService != null assert controller2.simpleService != null }

Testing Mime Type Handling

You can test mime type handling and the withFormat method quite simply by setting the response's format attribute:

// controller action
def sayHello() {
    def data = [Hello:"World"]
    withFormat {
        xml { render data as XML }
        html data
    }
}

// test
void testSayHello() {
    response.format = 'xml'
    controller.sayHello()

String expected = '<?xml version="1.0" encoding="UTF-8"?>' + '<map><entry key="Hello">World</entry></map>'

assert expected == response.text }

Testing Duplicate Form Submissions

Testing duplicate form submissions is a little bit more involved. For example if you have an action that handles a form such as:

def handleForm() {
    withForm {
        render "Good"
    }.invalidToken {
        render "Bad"
    }
}

you want to verify the logic that is executed on a good form submission and the logic that is executed on a duplicate submission. Testing the bad submission is simple. Just invoke the controller:

void testDuplicateFormSubmission() {
    controller.handleForm()
    assert "Bad" == response.text
}

Testing the successful submission requires providing an appropriate SynchronizerToken:

import org.codehaus.groovy.grails.web.servlet.mvc.SynchronizerToken
...

void testValidFormSubmission() { def token = SynchronizerToken.store(session) params[SynchronizerToken.KEY] = token.currentToken.toString()

controller.handleForm() assert "Good" == response.text }

If you test both the valid and the invalid request in the same test be sure to reset the response between executions of the controller:

controller.handleForm() // first execution
…
response.reset()
…
controller.handleForm() // second execution

Testing File Upload

You use the GrailsMockMultipartFile class to test file uploads. For example consider the following controller action:

def uploadFile() {
    MultipartFile file = request.getFile("myFile")
    file.transferTo(new File("/local/disk/myFile"))
}

To test this action you can register a GrailsMockMultipartFile with the request:

void testFileUpload() {
    final file = new GrailsMockMultipartFile("myFile", "foo".bytes)
    request.addFile(file)
    controller.uploadFile()

assert file.targetFileLocation.path == "/local/disk/myFile" }

The GrailsMockMultipartFile constructor arguments are the name and contents of the file. It has a mock implementation of the transferTo method that simply records the targetFileLocation and doesn't write to disk.

Testing Command Objects

Special support exists for testing command object handling with the mockCommandObject method. For example consider the following action:

def handleCommand(SimpleCommand simple) {
    if (simple.hasErrors()) {
        render "Bad"
    }
    else {
        render "Good"
    }
}

To test this you mock the command object, populate it and then validate it as follows:

void testInvalidCommand() {
    def cmd = mockCommandObject(SimpleCommand)
    cmd.name = '' // doesn't allow blank names

cmd.validate() controller.handleCommand(cmd)

assert response.text == 'Bad' }

Testing Calling Tag Libraries

You can test calling tag libraries using ControllerUnitTestMixin, although the mechanism for testing the tag called varies from tag to tag. For example to test a call to the message tag, add a message to the messageSource. Consider the following action:

def showMessage() {
    render g.message(code: "foo.bar")
}

This can be tested as follows:

void testRenderBasicTemplateWithTags() {
    messageSource.addMessage("foo.bar", request.locale, "Hello World")

controller.showMessage()

assert response.text == "Hello World" }