【问题标题】:testing grails data binding测试 grails 数据绑定
【发布时间】:2011-03-18 12:03:20
【问题描述】:

我编写了以下 Grails 控制器

class CategoryController {

    def create = {
        def newCategory = new CategoryCommand()
        bindData(newCategory, params)
        [newCategory: newCategory]
    }
}

class CategoryCommand {

    String name
    String seoName
}

我编写了这个单元测试来测试数据绑定:

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

    }
}

但是当controller.create() 被调用时我得到了这个异常:

方法没有签名:com.example.CategoryController.bindData() 适用于参数类型:(com.example.CategoryCommand, org.codehaus.groovy.grails.web.taglib.GroovyPageAttributes) 值:[com.example .CategoryCommand@7860e7d2, [:]]

我尝试将其作为集成测试运行,但结果相同。

【问题讨论】:

    标签: unit-testing grails groovy


    【解决方案1】:

    对...我挖了一点,发现this blog page 上面写着(大约一半):


    注意:ControllerUnitTestCase 不是 支持一些动态方法。为了 实例:绑定数据()。那么更好 使用集成测试,或者你可以 将此方法添加到控制器:

    this.controller.metaClass.bindData = { obj, params ->  
      params.each { key, value ->  
        obj."$key" = value  
      }  
    }  
    

    或者,我查看了Grails source code,并模拟它以执行相同的as what Grails does,我认为您需要这样做:

    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[] ) ;
    }
    

    (我认为 -- 没有测试过)

    【讨论】:

    • 我使用的是 Grails 1.3.6,似乎没有字段BindDynamicMethod.BIND_DATA_METHOD,所以第二个代码 sn-p 不起作用。您提供的链接目前无法使用,所以我看不到您是从哪里获得的
    • 第一个代码 sn-p 的缺点是在测试代码中重新实现了我正在尝试测试的内容,即数据绑定
    【解决方案2】:

    如前所述,使用 BindDynamicMethod 模仿 Grails 是可行的。这适用于 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[])
        }
    
    }
    

    这是从org/codehaus/groovy/grails/plugins/web/ControllersGrailsPlugin.groovy复制而来的,所以它支持bindData的所有形式。

    希望即将推出的 Grails 1.4 和 testing mixins 会改善这种情况。

    【讨论】:

    【解决方案3】:

    这里有两种可能的解决方案:

    1. 尝试再次将其作为集成测试运行。 ;) 我将您的测试类移至测试/集成文件夹,它通过了我。我也在运行 Grails 1.3.6。

    2. 将您的控制器更改为不使用 bindData。这个控制器动作相当于你现在拥有的,它会通过你的单元测试:

      def create = {
          def newCategory = new CategoryCommand(params)
          [newCategory: newCategory]
      }
      

      一个缺点是只有 bindData 可以为关联对象绑定数据,如果您有一个参数名称,例如“myAssociation.myProperty”。

    【讨论】:

      【解决方案4】:

      ControllerUnitTestCase 不支持bindData() 方法。

      JIRA 中存在一个问题:
      Would like to see a mocked bindData() method on ControllerUnitTestCase

      我编写了通过单元测试的最少代码:

      controller.metaClass.bindData = { obj, params ->
          obj.properties = params
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-01-20
        • 1970-01-01
        • 2011-08-06
        • 1970-01-01
        相关资源
        最近更新 更多