Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 448
Dónal Avatar asked Mar 18 '11 12:03

Dónal


2 Answers

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)

like image 85
tim_yates Avatar answered Oct 14 '22 17:10

tim_yates


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.

like image 44
Gustavo Giráldez Avatar answered Oct 14 '22 17:10

Gustavo Giráldez