如何在grails 2.2.4中模拟控制器中的私有方法

时间:2013-09-21 15:33:11

标签: unit-testing grails groovy mocking

我有一个私有方法,使用元类在grails 1.3.7中进行了模拟,但现在我将grails版本升级到2.2.4,同样的模拟失败。

测试方法调用私有方法

   private def MyPrivateMeth1(def arg1, def arg2) {
...
}

Mocking就是这样的

MyController.metaClass.private.MyPrivateMeth1 = { a, b ->
... 
}

4 个答案:

答案 0 :(得分:7)

尝试使用@TestFor注释,它将为您提供一个控制器变量。然后你可以改变它的元类,以及合并Kamil Mikolajczyk和araxn1d的建议。所以,你的整个测试应该是这样的:

@TestFor(MyController) 
class MyControllerTests { 

    setup() {
        controller.metaClass.MyPrivateMeth1 = {def arg1, def arg2 -> 
            //you can make your mocked method return whatever you wish here
            return [key1: "foo", key2: "bar"] 
        }
    }
    void testForSomething() {
        //Some code here that invokes an action or two in your controller which in turn invokes the private method
    }
}

确保在模拟方法的参数正确上使用def(或String,Long或任何其他声明),因为它们位于控制器中的实际私有方法或测试中将首先尝试使用该类的普通私有方法。

以下是Spock的类似测试:

import spock.lang.Specification
import spock.lang.Unroll
import grails.test.mixin.*
import org.junit.*
@TestFor(MyController) 
class MyControllerSpec {

    def "test that thing"() {
        setup:
            controller.metaClass.MyPrivateMeth1 = {def arg1, def arg2 -> return someOutput }
        expect:
            controller.someAction() == expectedRender
        where: 
            someOutput                              | expectedRender
            [key1: "foo", key2: "bar"]              | "expected render from controller"
    }
}

答案 1 :(得分:1)

似乎你需要声明闭包参数的类型(如果参数有实际类型,则为100%,例如Long,但不确定def,但你需要尝试):

MyController.metaClass.MyPrivateMeth1 = { def a, def b -> ... }

答案 2 :(得分:0)

我相信你不需要.private.部分

MyController.metaClass.MyPrivateMeth1 = { a, b -> ... }

应该足够了,但我会明确指定参数类型

顺便说一下,你应该保持java命名约定 - 方法名称应该以小写字符

开头

答案 3 :(得分:0)

对于单元测试,我使用了Reflection作为私有方法。类似的东西应该有用......

Method method = BehaviourService.getDeclaredMethod("behaviourValidConstraints",User.class,Behaviour.class)
method.setAccessible(true)
boolean valid = ((Boolean)method.invoke(service, user,b)).booleanValue()

首先使用getDeclaredMethod设置名称和参数类型的方法,将其设置为可访问,最后用method.invoke调用它,传递具有方法和参数的对象。结果是一个Object,所以你必须投射它。

我知道必须有一个更好的解决方案,但这个是我发现的唯一有效的解决方案

修改:抱歉,上面的内容是调用私有方法。 我认为在做之前我已经嘲笑了一种私人方法......

MyController.metaClass.myPrivateMeth1 { a, b ->
... 
}

就像你写的那样但没有.private和=符号。另外,正如Kamil所说,你应该遵循方法名称的java命名约定......