Grails JIRA

  • Log In Access more options
    • Online Help
    • GreenHopper Help
    • Agile Answers
    • Keyboard Shortcuts
    • About JIRA
    • JIRA Credits
    • What’s New
  • Dashboards Access more options (Alt+d)
  • Projects Access more options (Alt+p)
  • Issues Access more options (Alt+i)
  • Agile
Grails
  • Grails
  • GRAILS-787 Top level task: Improve Grails unit t...
  • GRAILS-3408

Support for unit testing URL Mappings

  • Log In
  • Views
    • XML
    • Word
    • Printable

Details

  • Type: Sub-task Sub-task
  • Status: Closed Closed
  • Priority: Major Major
  • Resolution: Fixed
  • Affects Version/s: None
  • Fix Version/s: 1.1-beta1
  • Component/s: Testing
  • Labels:
    None

Description

For apps with complex url mappings, it''s going to be necessary to allow testing of the mappings.

Consider the mappings...

"/$version"(controller: "version", action: "show") {
constraints

{ version(matches:/^\d.*/) }

}
"/$controller/$action?/$id?"

The test cases might look like...

class UrlMappingsTest extends GrailsUrlMappingTest {

void testVersionMappings() {
assertUrlMapping("/1.0")

{ assertEquals("version", controller) assertEquals("show", action) assertEquals("1.0", params.version) }

assertUrlMapping("/other")

{ assertEquals("other", controller) }

}
}

  • Options
    • Sort By Name
    • Sort By Date
    • Ascending
    • Descending
    • Download All

Attachments

  1. File
    GrailsUrlMappingsTestCase.groovy
    25/Sep/08 9:39 AM
    8 kB
    Luke Daley
  2. File
    GrailsUrlMappingTestCaseTests.groovy
    25/Sep/08 9:48 AM
    8 kB
    Luke Daley
  3. File
    UrlMappingsTests.groovy
    19/Sep/08 11:43 AM
    2 kB
    Glenn Saqui

Activity

Ascending order - Click to sort in descending order
  • All
  • Comments
  • Work Log
  • History
  • Activity
  • Git Commits
Hide
Permalink
Luke Daley added a comment - 19/Sep/08 10:32 AM

I have looked through how url mappings work, doesn't seem like it would be too hard to add testing support.

One thing I don't know though is how to get hold of the grailsUrlMappingsHolderBean in a test scenario. Perhaps there should be a 'GrailsIntegrationTest' class that somehow either gets beans injected via spring, or has access to those beans somehow.

Show
Luke Daley added a comment - 19/Sep/08 10:32 AM I have looked through how url mappings work, doesn't seem like it would be too hard to add testing support. One thing I don't know though is how to get hold of the grailsUrlMappingsHolderBean in a test scenario. Perhaps there should be a 'GrailsIntegrationTest' class that somehow either gets beans injected via spring, or has access to those beans somehow.
Hide
Permalink
Glenn Saqui added a comment - 19/Sep/08 11:43 AM

Here is an example of how I am testing url mappings. It's still missing a few things that I will add to on Monday but it's a place to start.

Glenn

Show
Glenn Saqui added a comment - 19/Sep/08 11:43 AM Here is an example of how I am testing url mappings. It's still missing a few things that I will add to on Monday but it's a place to start. Glenn
Hide
Permalink
Luke Daley added a comment - 21/Sep/08 1:48 AM

Here are my thoughts.

class UrlMappingTests extends GrailsUrlMappingTestCase {
def mappings = UrlMappings

/* tests */
}

or

class UrlMappingTests extends GrailsUrlMappingTestCase {
def mappings = [UrlMappings, OtherUrlMappings]

/* tests */
}

and if `mappings` is omitted then all mappings are loaded, considering that I think this would be the most common case.

I am also trying choose between the following...

void testMapping() {
"/module/list"(controller: "module", action: "list")
}

and...

void testMapping() {
assertUrlMapping("/module/list", controller: "module", action: "list")
}

Also trying to decide the best approach to asserting params.

With mappings like...

mappings = {
"/$groovy/$module/ivy.xml"(controller:"module", action: "ivy")
}

We could do...

void testMapping() {
assertUrlMapping("/1.5.4/someModule/ivy.xml", controller: "module", action: "ivy", groovy: "1.5.4", module: "someModule")
}

Or something like...

void testMapping() {
assertUrlMapping("/1.5.4/someModule/ivy.xml", controller: "module", action: "ivy")

{ groovy = "1.5.4" module = "someModule" }
}

Or...

void testMapping() {
assertUrlMapping("/1.5.4/someModule/ivy.xml", controller: "module", action: "ivy") { assertEquals("1.5.4", groovy) assertEquals("someModule", module) }
}

I am personally leaning towards...

void testMapping() {
"/1.5.4/someModule/ivy.xml"(controller: "module", action: "ivy") { groovy = "1.5.4" module = "someModule" }

}

Which is the most concise, but probably not the most intuitive.

Show
Luke Daley added a comment - 21/Sep/08 1:48 AM Here are my thoughts. class UrlMappingTests extends GrailsUrlMappingTestCase { def mappings = UrlMappings /* tests */ } or class UrlMappingTests extends GrailsUrlMappingTestCase { def mappings = [UrlMappings, OtherUrlMappings] /* tests */ } and if `mappings` is omitted then all mappings are loaded, considering that I think this would be the most common case. I am also trying choose between the following... void testMapping() { "/module/list"(controller: "module", action: "list") } and... void testMapping() { assertUrlMapping("/module/list", controller: "module", action: "list") } Also trying to decide the best approach to asserting params. With mappings like... mappings = { "/$groovy/$module/ivy.xml"(controller:"module", action: "ivy") } We could do... void testMapping() { assertUrlMapping("/1.5.4/someModule/ivy.xml", controller: "module", action: "ivy", groovy: "1.5.4", module: "someModule") } Or something like... void testMapping() { assertUrlMapping("/1.5.4/someModule/ivy.xml", controller: "module", action: "ivy") { groovy = "1.5.4" module = "someModule" } } Or... void testMapping() { assertUrlMapping("/1.5.4/someModule/ivy.xml", controller: "module", action: "ivy") { assertEquals("1.5.4", groovy) assertEquals("someModule", module) } } I am personally leaning towards... void testMapping() { "/1.5.4/someModule/ivy.xml"(controller: "module", action: "ivy") { groovy = "1.5.4" module = "someModule" } } Which is the most concise, but probably not the most intuitive.
Hide
Permalink
Luke Daley added a comment - 21/Sep/08 1:51 AM

Here are my thoughts.

class UrlMappingTests extends GrailsUrlMappingTestCase {
def mappings = UrlMappings
}

or

class UrlMappingTests extends GrailsUrlMappingTestCase {
def mappings = [UrlMappings, OtherUrlMappings]
}

and if `mappings` is omitted then all mappings are loaded, considering that I think this would be the most common case.

I am also trying choose between the following...

void testMapping() {
	"/module/list"(controller: "module", action: "list")
}

and...

void testMapping() {
	assertUrlMapping("/module/list", controller: "module", action: "list")
}

Also trying to decide the best approach to asserting params.

With mappings like...

mappings = {
	"/$groovy/$module/ivy.xml"(controller:"module", action: "ivy")
}	

We could do...

void testMapping() {
	assertUrlMapping("/1.5.4/someModule/ivy.xml", controller: "module", action: "ivy", groovy: "1.5.4", module: "someModule")
}

Or something like...

void testMapping() {
	assertUrlMapping("/1.5.4/someModule/ivy.xml", controller: "module", action: "ivy") {
		groovy = "1.5.4"
		module = "someModule"
	}
}

Or...

void testMapping() {
	assertUrlMapping("/1.5.4/someModule/ivy.xml", controller: "module", action: "ivy") {
		assertEquals("1.5.4", groovy)
		assertEquals("someModule", module)
	}
}

I am personally leaning towards...

void testMapping() {
	"/1.5.4/someModule/ivy.xml"(controller: "module", action: "ivy") {
		groovy =  "1.5.4"
		module = "someModule"
	}
}

Which is the most concise, but probably not the most intuitive.

Show
Luke Daley added a comment - 21/Sep/08 1:51 AM Here are my thoughts. class UrlMappingTests extends GrailsUrlMappingTestCase { def mappings = UrlMappings } or class UrlMappingTests extends GrailsUrlMappingTestCase { def mappings = [UrlMappings, OtherUrlMappings] } and if `mappings` is omitted then all mappings are loaded, considering that I think this would be the most common case. I am also trying choose between the following... void testMapping() { "/module/list" (controller: "module" , action: "list" ) } and... void testMapping() { assertUrlMapping( "/module/list" , controller: "module" , action: "list" ) } Also trying to decide the best approach to asserting params. With mappings like... mappings = { "/$groovy/$module/ivy.xml" (controller: "module" , action: "ivy" ) } We could do... void testMapping() { assertUrlMapping( "/1.5.4/someModule/ivy.xml" , controller: "module" , action: "ivy" , groovy: "1.5.4" , module: "someModule" ) } Or something like... void testMapping() { assertUrlMapping( "/1.5.4/someModule/ivy.xml" , controller: "module" , action: "ivy" ) { groovy = "1.5.4" module = "someModule" } } Or... void testMapping() { assertUrlMapping( "/1.5.4/someModule/ivy.xml" , controller: "module" , action: "ivy" ) { assertEquals( "1.5.4" , groovy) assertEquals( "someModule" , module) } } I am personally leaning towards... void testMapping() { "/1.5.4/someModule/ivy.xml" (controller: "module" , action: "ivy" ) { groovy = "1.5.4" module = "someModule" } } Which is the most concise, but probably not the most intuitive.
Hide
Permalink
Luke Daley added a comment - 21/Sep/08 5:46 PM

Another question is whether we be strict about controllers and views that don't exist. Maybe this should be an option...

class UrlMappingsTests extends GrailsUrlMappingsTestCase {
    testMappings() {
        "/someController/someAction"(controller: "someController", action: "someAction")
    }
}

Assuming `someController` doesn't exist, this would fail. But this would pass in the same situation...

class UrlMappingsTests extends GrailsUrlMappingsTestCase {
    def strict = false 
    testMappings() {
        "/someController/someAction"(controller: "someController", action: "someAction")
    }
}
Show
Luke Daley added a comment - 21/Sep/08 5:46 PM Another question is whether we be strict about controllers and views that don't exist. Maybe this should be an option... class UrlMappingsTests extends GrailsUrlMappingsTestCase { testMappings() { "/someController/someAction" (controller: "someController" , action: "someAction" ) } } Assuming `someController` doesn't exist, this would fail. But this would pass in the same situation... class UrlMappingsTests extends GrailsUrlMappingsTestCase { def strict = false testMappings() { "/someController/someAction" (controller: "someController" , action: "someAction" ) } }
Hide
Permalink
Luke Daley added a comment - 25/Sep/08 9:39 AM

Attaching GrailsUrlMappingTestCase and GrailsUrlMappingTestCaseTests.

As far as I am aware this is feature complete.

Show
Luke Daley added a comment - 25/Sep/08 9:39 AM Attaching GrailsUrlMappingTestCase and GrailsUrlMappingTestCaseTests . As far as I am aware this is feature complete.
Hide
Permalink
Luke Daley added a comment - 25/Sep/08 9:48 AM

Added some more tests for param assertions.

Show
Luke Daley added a comment - 25/Sep/08 9:48 AM Added some more tests for param assertions.
Hide
Permalink
Luke Daley added a comment - 25/Sep/08 9:58 AM

So how this is working at the moment.

It introduces three assertion methods (but each has a closure variant)

  • assertForwardUrlMapping()
  • assertReverseUrlMapping()
  • assertUrlMapping()

assertUrlMapping() calls the forward assertion, and if it makes sense calls the reverse assertion.

With a mapping like...

"/$param1/$param2"(controlller: "c", action: "a")

you test like this...

assertUrlMapping("/value1/value2", controller: "c", action: "a") {
    param1 = "value1"
    param2 = "value2"
}

All the assert*UrlMapping methods have the same signature.

By default, all of the url mappings in your application will be loaded for testing. However, in your test case you can specify a mappings property that can be; a list of UrlMapping classes, a single UrlMapping class, or a closure defining some mappings.

It is also noteworthy that these tests actually match against the controllers, actions and views in the grails app. So if the mapping assertion is actually correct, but it specifies a controller, action or view that doesn't exist, the assertion will fail. This way, url mappings that lead to 404's will be caught.

Show
Luke Daley added a comment - 25/Sep/08 9:58 AM So how this is working at the moment. It introduces three assertion methods (but each has a closure variant) assertForwardUrlMapping() assertReverseUrlMapping() assertUrlMapping() assertUrlMapping() calls the forward assertion, and if it makes sense calls the reverse assertion. With a mapping like... "/$param1/$param2" (controlller: "c" , action: "a" ) you test like this... assertUrlMapping( "/value1/value2" , controller: "c" , action: "a" ) { param1 = "value1" param2 = "value2" } All the assert*UrlMapping methods have the same signature. By default, all of the url mappings in your application will be loaded for testing. However, in your test case you can specify a mappings property that can be; a list of UrlMapping classes, a single UrlMapping class, or a closure defining some mappings. It is also noteworthy that these tests actually match against the controllers, actions and views in the grails app. So if the mapping assertion is actually correct, but it specifies a controller, action or view that doesn't exist, the assertion will fail. This way, url mappings that lead to 404's will be caught.
Hide
Permalink
Luke Daley added a comment - 26/Sep/08 4:09 AM

Should I just commit this to the testing plugin?

Show
Luke Daley added a comment - 26/Sep/08 4:09 AM Should I just commit this to the testing plugin?
Hide
Permalink
Peter Ledbrook added a comment - 27/Sep/08 6:29 AM

I have another suggestion before you do What do you think of using "==" instead of "=" in the closure? In other words:

assertUrlMapping("/value1/value2", controller: "c", action: "a") {
    param1 == "value1"
    param2 == "value2"
}

That matches better with the normal semantics of "==" and "=", plus you could use other comparators:

assertUrlMapping("/value1/value2", controller: "c", action: "a") {
    param1 =~ "value\\d"
    param2 =~ "value\\d"
}

BTW, thanks for all the work on this. Looks good!

Show
Peter Ledbrook added a comment - 27/Sep/08 6:29 AM I have another suggestion before you do What do you think of using "==" instead of "=" in the closure? In other words: assertUrlMapping( "/value1/value2" , controller: "c" , action: "a" ) { param1 == "value1" param2 == "value2" } That matches better with the normal semantics of "==" and "=", plus you could use other comparators: assertUrlMapping( "/value1/value2" , controller: "c" , action: "a" ) { param1 =~ "value\\d" param2 =~ "value\\d" } BTW, thanks for all the work on this. Looks good!
Hide
Permalink
Luke Daley added a comment - 28/Sep/08 1:55 AM

I thought about this, it would be quite tricky to pull off. Thinking about this more, if the documentation is worded right, then assignment actually makes more sense. It is also more orthogonal with how the controller and action params work.

If the closure is documented as a "DSL" for specifying the parameter values which will then be asserted for equality, then it kind of makes sense. This is opposed to thinking about the closure as code to be executed. Also, the user doesn't do any comparison of the controller or action params, they just specify them, which using the current approach is the same for params.

However, I do see how this is a bit weird until you get used to the idea. The only other idea I have is to just use another map key

assertUrlMapping("/value1/value2", controller: "c", action: "a", params: [param1: "value1", param2: "value2"])

That has a certain charm to it, but it's not as readable in my opinion.

Show
Luke Daley added a comment - 28/Sep/08 1:55 AM I thought about this, it would be quite tricky to pull off. Thinking about this more, if the documentation is worded right, then assignment actually makes more sense. It is also more orthogonal with how the controller and action params work. If the closure is documented as a "DSL" for specifying the parameter values which will then be asserted for equality , then it kind of makes sense. This is opposed to thinking about the closure as code to be executed. Also, the user doesn't do any comparison of the controller or action params, they just specify them, which using the current approach is the same for params. However, I do see how this is a bit weird until you get used to the idea. The only other idea I have is to just use another map key assertUrlMapping( "/value1/value2" , controller: "c" , action: "a" , params: [param1: "value1" , param2: "value2" ]) That has a certain charm to it, but it's not as readable in my opinion.
Hide
Permalink
Peter Ledbrook added a comment - 29/Sep/08 2:32 AM

The map approach is readable with appropriate indentation But I'm more than happy to go with the current approach - to be honest "==" would look a little strange. Feel free to commit it if you haven't done so already.

Show
Peter Ledbrook added a comment - 29/Sep/08 2:32 AM The map approach is readable with appropriate indentation But I'm more than happy to go with the current approach - to be honest "==" would look a little strange. Feel free to commit it if you haven't done so already.
Hide
Permalink
Luke Daley added a comment - 29/Sep/08 4:38 AM

Committed, where should I write the documentation?

Show
Luke Daley added a comment - 29/Sep/08 4:38 AM Committed, where should I write the documentation?
Hide
Permalink
Peter Ledbrook added a comment - 29/Sep/08 12:08 PM

If you're ready to write it, add it to a "Testing Plugin" page on the Grails website. I'll transfer that documentation across to the reference guide for 1.1 at some point.

Show
Peter Ledbrook added a comment - 29/Sep/08 12:08 PM If you're ready to write it, add it to a "Testing Plugin" page on the Grails website. I'll transfer that documentation across to the reference guide for 1.1 at some point.
Hide
Permalink
Luke Daley added a comment - 02/Oct/08 7:34 AM

Documentation @ http://www.grails.org/Testing+URL+Mappings.

As far as I am aware, this ticket is now resolved.

Show
Luke Daley added a comment - 02/Oct/08 7:34 AM Documentation @ http://www.grails.org/Testing+URL+Mappings . As far as I am aware, this ticket is now resolved.
Hide
Permalink
Peter Ledbrook added a comment - 02/Oct/08 8:10 AM

Thanks for implementing this Luke.

Show
Peter Ledbrook added a comment - 02/Oct/08 8:10 AM Thanks for implementing this Luke.
Hide
Permalink
Graeme Rocher added a comment - 18/May/11 8:41 AM

Bulk closing bunch of resolved issues

Show
Graeme Rocher added a comment - 18/May/11 8:41 AM Bulk closing bunch of resolved issues

People

  • Assignee:
    Graeme Rocher
    Reporter:
    Luke Daley
Vote (2)
Watch (3)

Dates

  • Created:
    18/Sep/08 7:47 PM
    Updated:
    18/May/11 8:41 AM
    Resolved:
    02/Oct/08 8:10 AM

Agile

  • View on Board
  • Atlassian JIRA (v5.2.1#813-sha1:277a546)
  • Report a problem
  • Powered by a free Atlassian JIRA open source license for Grails project. Try JIRA - bug tracking software for your team.