Mockito:将答案附加到任意对象实例的每个方法

Mockito: Attach Answer to every method of arbitrary Object instance

我有以下情况:

我想将 Answer 附加到特定 class 实例的每个方法调用。例如 class

public class Example {
    public int example1() { /* */ }
    public int example2(Object a) { /* */ }
    public int example3(Object a, Integer b) { /* */ }
    public int example4(int a) { /* */ }
}

我想做以下事情

public Example attachToExample(Example ex) {
    Example spy = Mockito.spy(ex);
    Answer<Object> answer = /* */;
    doAnswer(answer).when(spy).example1();
    doAnswer(answer).when(spy).example2(any());
    doAnswer(answer).when(spy).example3(any(), any());
    doAnswer(answer).when(spy).example4(anyInt());
    return spy;
}

这行得通,但我想做的是将其推广到不仅 Example 个实例,而且推广到任意对象。

所以我想做的是

public Object attachToExample(Object o) {
    Object spy = Mockito.spy(o);
    Answer<Object> answer = /* */;
    for(Method m : o.getClass().getMethods()) {
        /* skipping methods that cannot be mocked (equals/hashCode/final/..) */

        doAnswer(answer).when(spy)./* Method m with according arguments */;
    }
    return spy;
}

我需要做的是构造参数匹配器 any/anyInt/.. 取决于每个方法的参数数量及其类型(primitive/non 原语).理想情况下,我会创建一个这样的参数列表:

Class<?>[] params = m.getParameterTypes();
ArrayList<Object> args = new ArrayList<>();
for (Class<?> param : params) {
    if ("int".equals(param.toString())) {
        args.add(ArgumentMatchers.anyInt());
    } else { // Cases for other primitive types left out.
        args.add(ArgumentMatchers.any()); // Found non primitive. We can use 'any()'
    }
}
            
try {
    doAnswer(answer).when(spy).getClass().getMethod(m.getName(), m.getParameterTypes())
            .invoke(spy, args.toArray());
} catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
    e.printStackTrace();
}

这不起作用,因为不支持在存根之外使用参数匹配器,但我希望这能说明我想做什么。

是否有任何方法可以完成这项工作,或者是否有不同的方法来存档我想做的事情?

好的,我找到了一种方法来做我想做的事:

虽然参数数组不能在 invoke 调用之前构建,但我们可以通过外部方法调用来实现,如下所示:

try {
    doAnswer(answer).when(spy).getClass().getMethod(m.getName(), m.getParameterTypes())
            .invoke(spy, constructArguments(m));
} catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
    e.printStackTrace();
}

其中 constructArguments 如下:

private static Object[] getArgumentMatcher(Method m) {
    Class<?>[] types = m.getParameterTypes();

    Object[] res = new Object[types.length];
    for(int i = 0; i < types.length; ++i) {
        if (types[i].isPrimitive()) {
            // For primitives we need to specify the type explicitly ¯\_(ツ)_/¯
            res[i] = any(types[i]);
        } else {
            res[i] = any();
        }
    }
    return res;
}