If you are writing a unit or integration test in grails and mocking the controller, which can be done either by extending ControllerUnitTestCase (or using mockController) you might run into this issue. As you probably know, there are multiple ways to render JSON in grails. One is to take a normal map and render as JSON:
def jsonMap = [success:true]
render jsonMap as JSON
This will work fine everywhere, because you're effectively passing it in as a converter. However, if you use the more verbose (and in many ways more powerful) rendering, you will run into issues:
render(contentType: "text/json") {
success(true)
results {
result.each {
def period = it.getPeriod()
b(id: it.id,name: it.name)
}
}
}
In my case, my test looks like this:
assert JSON.parse(this.controller.response.contentAsString)?.success == true
This won't work, because the content on the mock response will be null. With a little digging into how mockController() works, you will find the problem lies in MockUtils. Specifically, in the method that handles a map with render options and a closure:
clazz.metaClass.render = {Map map, Closure c ->
renderArgs.putAll(map)
switch(map["contentType"]) {
case null:
break
case "application/xml":
case "text/xml":
def b = new StreamingMarkupBuilder()
if (map["encoding"]) b.encoding = map["encoding"]
def writable = b.bind(c)
delegate.response.outputStream << writable
break
default:
println "Nothing"
break
}
}
As you can see, the case of 'text/json' isn't handled. I was able to workaround this issue by creating a custom unit test case class to extend from the Grails ControllerUnitTestCase:
class CustomControllerTestCase extends ControllerUnitTestCase{
protected void setUp() {
super.setUp()
controller.class.metaClass.render = {Map map, Closure c ->
renderArgs.putAll(map)
switch(map["contentType"]) {
case null:
break
case "application/xml":
case "text/xml":
def b = new StreamingMarkupBuilder()
if (map["encoding"]) b.encoding = map["encoding"]
def writable = b.bind(c)
delegate.response.outputStream << writable
break
case "text/json":
new JSonBuilder(delegate.response).json(c)
break
default:
println "Nothing"
break
}
}
}
}
Since it's groovy, you could pretty much replace this anywhere by overriding your mock controller's meta class.
3 comments:
Thanks Lucas, this problem looked like it was going to start eating up the rest of my afternoon. Your solution did the trick.
you saved my day :-)
A focused on client will, by definition, see how your items can assist them.your commercial duplicate won't just visit this site captivate their investment, it will make them consider, "That's what I've been searching for!" And that sort of reaction makes advertising a snap.
Post a Comment