Grails - Testing URL Mappings

Testing URL Mappings

Basics

URL mapping test cases must extend grails.test.GrailsUrlMappingsTestCase and should live in test/integration .

URL mapping tests take an application relative URL, and assert that; it is mapped to a controller and/or action or view, the params contained within the URL are extracted as expected, and that the destination actually exists. That is, if you are testing a URL mapping that would map to the controller BookController , and you assert this controller but it doesn't actually exist… the assertion will fail. This applies to controllers, actions and views. It also tests for mapping to default actions of controllers where applicable.

Specifying the mappings to test

By default, the test case will load all of the mappings that you have defined in your application. However if you have your mappings defined over several classes you can specify the mappings to test by defining a static mappings property as either a single url mappings class or a list or url mapping classes.

Example…

class UrlMappingTests extends GrailsUrlMappingsTestCase
{
   static mappings = UrlMappings
}

Or…

class UrlMappingTests extends GrailsUrlMappingsTestCase
{
   static mappings = [UrlMappings, OtherUrlMappings]
}

forward URL mappings">

Asserting forward URL mappings

The term "forward URL mapping" refers to asserting that a given URL maps to a handler within your grails app, whether that be an action or a view.

Given the following standard URL mappings…

/controller/action?/id?()

And the following controller…

class BookController
{
    def defaultAction = "list"

def list = { /* */ } def save = { /* */ } def show = { /* */ } }

We would test them like…

assertForwardUrlMapping("/book", controller: "book", action: "list")
assertForwardUrlMapping("/book/save", controller: "book", action: "save")
assertForwardUrlMapping("/book/show", controller: "book", action: "show")

Remember that tests for actions or controllers that don't exist will fail…

assertForwardUrlMapping("/book/delete", controller: "book", action: "delete") // this will fail

Testing embedded parameters

@assertForwardUrlMapping()@ can optional take a closure that defines the embedded parameters to test for in the URL mapping…

assertForwardUrlMapping("/book/show/32", controller: "book", action: "show") {
    id = 32
}

Mapping to views

Views are tested for just like actions…

"/stats"(view: "stats")

assertForwardUrlMapping("/stats", view: "stats")

Testing HTTP response code mappings

HTTP response code mappings can be tested by specifying the HTTP response code as an integer…

assertForwardUrlMapping(500, view: "error")

reverse URL mappings">

Asserting reverse URL mappings

The term "reverse URL mapping" refers to asserting that redirecting or creating a link to the specified action or view with the specified parameters would map to the specified URL…

assertReverseUrlMapping("/book/show", controller: "book", action: "show")

Or…

assertReverseUrlMapping("/book/show/25", controller: "book", action: "show") {
    id = 25
}

All of the same rules that apply to testing the forward mappings apply to testing the reverse mappings.

forward and reverse mappings simultaneously">

Asserting the forward and reverse mappings simultaneously

The method assertUrlMappings() has the same signature as assertForwardUrlMapping() and assertReverseUrlMapping() . If applicable, the forward mapping is tested, then the reverse mapping. For the reverse mapping to be tested, a controller has to be specified and the URL must not be a http response code integer.

assertUrlMapping("/book/show/32", controller: "book", action: "show") {
    id = 32
}

Example

class UrlMappings {
    static mappings = {
        "/$user"(controller: "user", action: "show") {
            constraints {
                user(matches: /^[p{Digit}].*$/) // Users must start with a number
            }
        }
        "/$user/$msgid"(controller: "user", action: "showMessage") {
            constraints {
                user(matches: /^[p{Digit}].*$/) // Users must start with a number
            }
        }
        "/icon/$id?"(controller: "icon")
        "/credits"(controller: "credits", view: "credits")
        "500"(view: "error")
    }
}

Assuming all of those controllers, actions and views exist…

class UrlMappingsTests extends grails.test.GrailsUrlMappingsTestCase {

void testUserMappings() { assertUrlMapping("/123", controller: "user", action: "show") { user = "123" } assertUrlMapping("/456/abc", controller: "user", action: "showMessage") { user = "123" msgid = "abc" } }

void testAdminMappings() { assertUrlMapping("/icon", controller: "icon", action: "index") { // assuming 'index' is the default action of the admin controller id = null } assertUrlMapping("/icon/123", controller: "icon") { // can leave action out id = "123" } }

void testCreditsMappings() { assertUrlMapping("/credits", controller: "credits", view: "credits") }

void testErrorMappings() { assertUrlMapping(500, view: "error") }

}