Reputation: 168
In a Grails 2.1.1 application, I'm trying to unit test a controller that uses 'withFormat' to render the response either as HTML or JSON. However responding to the HTML content type always results in an empty response in my test, unless I wrap it in a closure and explicitly call 'render'. For JSON, it sends back the expected response.
Controller:
import grails.converters.JSON
class TestController {
def formatWithHtmlOrJson() {
withFormat {
html someContent:"should be HTML"
json {render new Expando(someContent:"should be JSON") as JSON}
}
}
Test:
@TestFor(TestController)
class TestControllerTests {
void testForJson() {
response.format = "json"
controller.formatWithHtmlOrJson()
println("resp: $response.contentAsString")
assert response.json.properties.someContent == "should be JSON"
}
void testForHtml() {
response.format = "html"
controller.formatWithHtmlOrJson()
println("resp: $response.contentAsString")
// fails here, response is empty
assert response.text
//never gets this far
assert response.contentAsString.contains("HTML")
}
}
As described above, for JSON this works, but for HTML I always get an empty response, unless I wrap the html check in a closure and explicitly call render, as below:
withFormat {
html {
render someContent:"should be HTML"
}
The docs suggest I shouldn't need to do this, e.g. :
withFormat {
html bookList: books
js { render "alert('hello')" }
xml { render books as XML }
}
from http://grails.org/doc/2.2.x/ref/Controllers/withFormat.html
Frustratingly, the grails docs on testing mention the use of withFormat but only give examples for testing xml/json, and nothing for the html response.
http://grails.org/doc/latest/guide/testing.html#unitTestingControllers
Can anyone explain this discrepancy, or how I might work around it in my tests?
Upvotes: 2
Views: 2156
Reputation: 168
Finally figured this out.
In the documentation (http://grails.org/doc/latest/guide/testing.html#unitTestingControllers), it mentions this way of testing a controller response under Testing Actions Which Return A Map :
import grails.test.mixin.*
@TestFor(SimpleController)
class SimpleControllerTests {
void testShowBookDetails() {
def model = controller.showBookDetails()
assert model.author == 'Alvin Plantinga'
}
}
The same approach works for controller methods that use withFormat.
So for my original example above:
withFormat {
html someContent:"should be HTML"
...
the test becomes:
void testForHtml() {
response.format = "html"
def model = controller.formatWithHtmlOrJson()
assert model.someContent == "should be HTML"
}
The documentation is a tad confusing as the withFormat section makes no mention of this approach.
Worth noting, should anyone else encounter this, that if the html block is inside a closure, the map isn't returned, but rather the value of the map entry, so for the controller code:
withFormat{
html{
someContent:"should be HTML"
}...
the test check becomes:
assert model == "should be HTML"
Alternatively, if you can modify the controller code, returning the result inside a map lets you use the dot notation to check the element value. For this code:
withFormat {
html {
[someContent:"should be HTML"]
}....
the test check is:
assert model.someContent == "should be HTML"
Also worth noting, in my original example that doesn't use a closure for the HTML type, you can't return the value as a map- it results in a compile error.
//Don't do this, won't compile
withFormat {
html [someContent:"should be HTML"]
...
Upvotes: 3
Reputation: 50265
Suggestion provided in the document does not use a closure
and has this verbiage "Grails searches for a view called grails-app/views/book/list.html.gsp and if that is not found fallback to grails-app/views/book/list.gsp".
When you use a closure
for html
then we have to either return a model
to the action or render
the content. Since in this case you are using the html
closure
the content has to be rendered.
Using a Closure (Your use case passed)
withFormat{
html {
test: "HTML Content" //This will not render any content
render(test: "HTML Content") //This has to be used
}
}
Upvotes: 0
Reputation: 5540
try
withFormat {
html {
println("html")
[new Expando(test:"html")]
}
}
Upvotes: 0