简体   繁体   中英

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 signature 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:

  1. 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.

  2. 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
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM