Loading...

May 16, 2013

Grails Goodness: Checking Results from Forward Action in Controller Unit Tests

In Grails we can write unit tests for controllers. We can check for example the results from a redirect() or render() method. To check the result from a forward() action we can use the forwardedUrl property of the mock response object. The format of the URL is very basic and has the following form: /grails/(controller)/(action).dispatch?(queryParameters). So we get the servlet representation of a Grails controller request. We can use this format to check if our forward() method is correct in the unit test.

First we create a simple controller with a index() action which invokes the forward() method with a action and parameters:

package com.mrhaki.grails

class BookController {

    def index() {
        forward action: 'show', params: [fromIndex: true, bookId: 200]
    }

}

Now we write a test (with Spock of course) to check the values from the forward() method in the mock response. The following specification contains code to parse the forwardedUrl property. We get the /controller/action as String and the parameters as Map object. Using Groovy syntax we can check for the values with forwardedUrl and forwardedParams:

package com.mrhaki.grails

import grails.test.mixin.TestFor
import spock.lang.Specification

@TestFor(BookController)
class BookControllerSpecification extends Specification {

    def "index must forward to show action"() {
        when:
        controller.index()

        then:
        forwardedUrl == '/book/show'
    }

    def "index must set parameter fromIndex to value true and forward"() {
        when:
        controller.index()

        then:
        forwardedParams.fromIndex == 'true'
    }

    def "index must set parameter bookId to 200 and forward"() {
        when:
        controller.index()

        then:
        forwardedParams.bookId == '200'
    }

    private getForwardedParams() {
        parseResponseForwardedUrl()[1]
    }

    private String getForwardedUrl() {
        parseResponseForwardedUrl()[0]
    }

    private parseResponseForwardedUrl() {
        // Pattern for forwardedUrl stored in response.
        def forwardedUrlPattern = ~/\/grails\/(.*)?\.dispatch\?*(.*)/

        // Match forwardedUrl in response with pattern.
        def matcher = response.forwardedUrl =~ forwardedUrlPattern

        def forwardUrl = null
        def forwardParameters = [:]

        if (matcher) {
            // Url is first group in pattern. We add '/' so it has the same format as redirectedUrl from response.
            forwardUrl = "/${matcher[0][1]}"

            // Parse parameters that are available in the forwardedUrl of the response.
            forwardParameters = parseResponseForwardedParams(matcher[0][2])
        }

        [forwardUrl, forwardParameters]
    }

    private parseResponseForwardedParams(final String queryString) {
        // Query string has format paramName=paramValue¶m2Name=param2Value. & is optional.
        def parameters = queryString.split('&')

        // Turn the paramName=paramValue parts into a Map.
        def forwardParameters = parameters.inject([:]) { result, parameter ->
            def (parameterName, parameterValue) = parameter.split('=')
            result[parameterName] = parameterValue
            result
        }
        forwardParameters
    }
}

Code written with Grails 2.2.2 and Spock 0.7-groovy-2.0