5

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

测试方法调用私有方法

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

嘲笑是这样的

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

4 回答 4

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"
    }
}
于 2013-11-26T22:09:59.020 回答
1

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

MyController.metaClass.MyPrivateMeth1 = { def a, def b -> ... }
于 2013-09-23T09:14:05.307 回答
0

对于单元测试,我将反射用于私有方法。类似的东西应该可以工作......

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命名约定......

于 2013-09-23T06:46:31.087 回答
0

我相信你不需要这个.private.角色

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

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

而且,顺便说一下,你应该保持java命名约定——方法名应该以小写字符开头

于 2013-09-22T11:21:12.273 回答