使用groovy.mock.interceptor检查输入参数

时间:2018-11-19 11:24:59

标签: unit-testing groovy

我正在尝试使用redis-3.2.4 $ rdb -c memory dump.rdb -k my.* database,type,key,size_in_bytes,encoding,num_elements,len_largest_element 0,list,"mylistkey",219,quicklist,7,6 0,sortedset,"mysortedsetkey",143,ziplist,6,5 0,hash,"myhashkey",115,ziplist,2,6 0,string,"mystringkey",88,string,13,13 0,string,"myhllkey",168,string,90,90 0,set,"mysetkey",452,hashtable,4,6 进行一些单元测试。我想断言,确实以某些特定值作为参数调用了函数。我找不到方法。有帮助吗?

这是它的样子:

redis-3.2.4 $ rdb -c memory dump.rdb -k my.* -f memory.csv
redis-3.2.4 $ head memory.csv 
database,type,key,size_in_bytes,encoding,num_elements,len_largest_element
0,list,"mylistkey",219,quicklist,7,6
0,sortedset,"mysortedsetkey",143,ziplist,6,5
0,hash,"myhashkey",115,ziplist,2,6
0,string,"mystringkey",88,string,13,13
0,string,"myhllkey",168,string,90,90
0,set,"mysetkey",452,hashtable,4,6

2 个答案:

答案 0 :(得分:0)

MockFor类无法实现预期的行为。忽略main方法有一个重要的效果-内部方法myFunction被执行,但是它在没有MockInterceptor的情况下发生。您可以在groovy.mock.MockProxyMetaClass的开头的invokeMethod类中放置一个断点(第74行),然后运行调试器以查看会发生什么。

public Object invokeMethod(final Object object, final String methodName, final Object[] arguments) {
    if (null == interceptor && !fallingThrough) {
        throw new RuntimeException("cannot invoke method '" + methodName + "' without interceptor");
    }
    Object result = FALL_THROUGH_MARKER;
    if (interceptor != null) {
        result = interceptor.beforeInvoke(object, methodName, arguments);
    }
    if (result == FALL_THROUGH_MARKER) {
        Interceptor saved = interceptor;
        interceptor = null;
        boolean savedFallingThrough = fallingThrough;
        fallingThrough = true;
        result = adaptee.invokeMethod(object, methodName, arguments);
        fallingThrough = savedFallingThrough;
        interceptor = saved;
    }
    return result;
}

foo.main()块中调用mock.use {}方法可为非null拦截器调用此方法。 interceptor.beforeInvoke()返回的结果等于FALL_THROUGH_MARKER,因为main方法被标记为已忽略。在这种情况下,拦截器会暂时设置为null,并且该方法会以常规方式调用-它会调用内部myFunction方法,但是由于null拦截器目前尚未记录此事实。

基本上,您将测试用例中的模拟对象视为模拟对象,而不是模拟对象。 Groovy标准模拟库不支持间谍对象,但您可以使用例如Spock Framework使用间谍对象编写测试。使用Spock,您在问题中显示的测试可能看起来像这样:

import spock.lang.Specification

class ExampleSpec extends Specification {

    static class MyClass {
        def main() {
            return myFunction(0, 0 ,0)
        }

        def myFunction(def a, def b, def c) {
            return '2'
        }
    }

    def "should call myFunction with specific parameters"() {
        given:
        def foo = Spy(MyClass)

        when:
        foo.main()

        then:
        1 * foo.myFunction(0, 0, 0)

        and:
        0 * foo.myFunction(1,0,0)
    }
}

它执行一个真实的foo.main()方法,但是它模拟foo.myFunction()方法并记录调用并测试该方法是否被正确的参数调用-它记录它被参数{{1}调用一次},并且未使用参数(0, 0, 0)调用它。

重要提示:如果您是从类而非接口创建模拟/间谍对象,则需要将(1, 0, 0)依赖项与Spock一起添加。

答案 1 :(得分:0)

好的,这是可行的,因为mock.demand.myFunction采用普通的Closure。 我最终得到了这样的东西:

import groovy.mock.interceptor.MockFor
import org.junit.Test

class MyClassTest extends GroovyTestCase {
    @Test
    void test_correctness_of_passed_arguments() {
        def mock = new MockFor(MyClass)
        mock.ignore('main')
        def res = []
        // the mocked function stores its values in `res` and returns '0'
        mock.demand.myFunction(4) {a, b, c ->
            res.add([a, b, c])
            '0'
        }
        mock.use {
            def foo = new MyClass()
            foo.main()  // <--- this is in there that it gets executed
        }
        mock.expect.verify()
        res[0] // <--- I can then access the values there
    }
}

在上面的示例中,我请求myFunction次被调用4