【问题标题】:How to mock private methods in controller in grails 2.2.4如何在grails 2.2.4中模拟控制器中的私有方法
【发布时间】:2013-09-21 15:33:11
【问题描述】:

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

要测试的方法调用了私有方法

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

Mocking 是这样的

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

【问题讨论】:

    标签: unit-testing grails groovy mocking


    【解决方案1】:

    尝试使用@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"
        }
    }
    

    【讨论】:

    • 您能否提供从模拟控制器方法返回 Map 的方法。这样做我得到了一些例外
    • 在我上面的例子中, someOutput 是模拟控制器方法返回的变量。在我的示例 where 子句中,我将其设置为字符串,但您也可以轻松地将其设置为 Map。我将更新示例。
    【解决方案2】:

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

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

    【讨论】:

    • ya...试过了,但似乎没有用...。我也试过 def strict = mockFor(MyController) strictControl.demand.MyPrivateMeth1 {User a,Account b -> ... . }
    【解决方案3】:

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

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

    应该够了,不过我会明确指定参数类型

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

    【讨论】:

    • 我试过了,但似乎没有用......我还缺少什么吗?
    【解决方案4】:

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

    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 和 = 符号。另外,正如 Kamil 所说,您应该遵循 java 方法名称的命名约定......

    【讨论】:

    • 这将调用我的控制器类中的私有方法,不是吗?我想模拟这个方法而不是调用它。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-05-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-04
    • 2020-01-01
    • 1970-01-01
    相关资源
    最近更新 更多