我有一个私有方法,它在 grails 1.3.7 中使用元类进行了模拟,但现在我将 grails 版本升级到 2.2.4,同样的模拟失败了。
测试方法调用私有方法
private def MyPrivateMeth1(def arg1, def arg2) {
...
}
嘲笑是这样的
MyController.metaClass.private.MyPrivateMeth1 = { a, b ->
...
}
我有一个私有方法,它在 grails 1.3.7 中使用元类进行了模拟,但现在我将 grails 版本升级到 2.2.4,同样的模拟失败了。
测试方法调用私有方法
private def MyPrivateMeth1(def arg1, def arg2) {
...
}
嘲笑是这样的
MyController.metaClass.private.MyPrivateMeth1 = { a, b ->
...
}
尝试使用 @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"
}
}
似乎您需要声明闭包参数的类型(例如,如果该参数具有实际类型,则为 100% Long
,但不确定 def,但您需要尝试):
MyController.metaClass.MyPrivateMeth1 = { def a, def b -> ... }
对于单元测试,我将反射用于私有方法。类似的东西应该可以工作......
Method method = BehaviourService.getDeclaredMethod("behaviourValidConstraints",User.class,Behaviour.class)
method.setAccessible(true)
boolean valid = ((Boolean)method.invoke(service, user,b)).booleanValue()
首先,您使用 getDeclaredMethod 设置名称和参数类型来获取方法,将其设置为可访问,最后使用 method.invoke 调用它,并传递具有该方法和参数的对象。结果是一个对象,因此您必须对其进行转换。
我知道必须有一个更好的解决方案,但这是我发现的唯一一个有效的解决方案
编辑:对不起,上面的内容是调用私有方法。我认为我在做之前已经嘲笑了一个私有方法......
MyController.metaClass.myPrivateMeth1 { a, b ->
...
}
就像你写的一样,但没有 .private 和 = 符号。另外,正如卡米尔所说,您应该遵循方法名称的java命名约定......
我相信你不需要这个.private.
角色
MyController.metaClass.MyPrivateMeth1 = { a, b -> ... }
应该足够了,但是我会明确指定参数类型
而且,顺便说一下,你应该保持java命名约定——方法名应该以小写字符开头