testing grails data binding
I've written the following Grails controller
class CategoryController {
def create = {
def newCategory = new CategoryCommand()
bindData(newCategory, params)
[newCategory: newCategory]
}
}
class CategoryCommand {
String name
String seoName
}
I've written this unit test to test the data binding:
class CategoryControllerTests extends ControllerUnitTestCase {
void testCreate() {
// A new ControllerCommand should be returned if invoked with no params
assertNotNull controller.create()
// If called with params, they should be bound
mockParams.name = 'snooker'
mockParams.seoName = 'snooker-loopy'
def model = controller.create()
CategoryCommand newCategory = model.newCategory
assertEquals 'snooker', newCategory.name
assertEquals 'snooker-loopy', newCategory.seoName
}
}
But I get this exception when controller.create()
is invoked:
No sig开发者_如何学Cnature of method: com.example.CategoryController.bindData() is applicable for argument types: (com.example.CategoryCommand, org.codehaus.groovy.grails.web.taglib.GroovyPageAttributes) values: [com.example.CategoryCommand@7860e7d2, [:]]
I tried running this as an integration test instead, but the result is the same.
Right...I did a bit of digging, and found this blog page which says (about half way down):
note:ControllerUnitTestCase not support some dynamic method. For instance: bindData(). Then is better use integration testing, or you can add this method to controller:
this.controller.metaClass.bindData = { obj, params ->
params.each { key, value ->
obj."$key" = value
}
}
Or, I had a look in the Grails source code, and to mock it to do the same as what Grails does, I think you'd need to do:
import org.codehaus.groovy.grails.web.metaclass.BindDynamicMethod
this.controller.metaClass.bindData = { obj, params ->
new BindDynamicMethod().invoke( delegate, BindDynamicMethod.BIND_DATA_METHOD, [ obj, params ] as Object[] ) ;
}
(I think -- Not tested it tho)
As mentioned previously, mimicking Grails by using BindDynamicMethod works. This works for me on Grails 1.3.5:
import org.codehaus.groovy.grails.web.metaclass.BindDynamicMethod
protected void setUp() {
def mc = controller.class.metaClass
def bind = new BindDynamicMethod()
mc.bindData = { Object target, Object args ->
bind.invoke(delegate, "bindData", [ target, args ] as Object[])
}
mc.bindData = { Object target, Object args, List disallowed ->
bind.invoke(delegate, "bindData", [ target, args, [ exclude: disallowed ]] as Object[])
}
mc.bindData = { Object target, Object args, List disallowed, String filter ->
bind.invoke(delegate, "bindData", [ target, args, [ exclude: disallowed ], filter ] as Object[])
}
mc.bindData = { Object target, Object args, Map includeExclude ->
bind.invoke(delegate, "bindData", [ target, args, includeExclude ] as Object[])
}
mc.bindData = { Object target, Object args, Map includeExclude, String filter ->
bind.invoke(delegate, "bindData", [ target, args, includeExclude, filter ] as Object[])
}
mc.bindData = { Object target, Object args, String filter ->
bind.invoke(delegate, "bindData", [ target, args, filter ] as Object[])
}
}
This is copied from org/codehaus/groovy/grails/plugins/web/ControllersGrailsPlugin.groovy
, and so it supports all forms of bindData
.
Hopefully the situation will improve with the upcoming Grails 1.4 and testing mixins.
Here are two possible solutions:
Try running it as an integration test again. ;) I moved your test class to the test/integration folder and it passed for me. I'm running Grails 1.3.6 too.
Change your controller to not use bindData. This controller action is equivalent to what you have now, and it will pass your unit test:
def create = { def newCategory = new CategoryCommand(params) [newCategory: newCategory] }
one downside is that only bindData can bind data for associated objects, if you have a parameter name such as "myAssociation.myProperty".
ControllerUnitTestCase
not support bindData()
method.
There is an issue open in JIRA:
Would like to see a mocked bindData() method on ControllerUnitTestCase
I write the minimum code to pass the unit test:
controller.metaClass.bindData = { obj, params ->
obj.properties = params
}
精彩评论