调用 Mockito.verify 编程方式

huangapple go评论78阅读模式
英文:

Call Mockito.verify programmatically

问题

以下是您要翻译的内容:

我有一个相当大的代码库,其中有许多装饰器类,通常将除一个方法之外的所有方法委托给一个代理对象,就像这样:

    class WrapperThing implements Thing {
       private final Thing delegate;
       WrapperThing(Thing thing){this.delegate=thing;}

       public boolean method1(){ return delegate.method1(); }
       public String method2(int arg1, boolean arg2){ return delegate.method2(arg1, arg2); }
       // 还有很多方法在这里,都委托给了代理
    }

现在,我正在为这些包装器创建单元测试,使用JUnit 5的@TestFactory,调用WrapperThing上的每个方法,并希望验证已经在包装的代理上调用了方法,该代理是一个Mockito模拟对象。

以下是我目前的代码:

    private void testMethodDelegation(final Method method) {
        D delegate = mock(delegateType);
        W wrapper = createWrapper(delegate);

        List<Object> args = new ArrayList<>(method.getParameterTypes().length + 1);
        args.add(wrapper);
        gatherMethodArgs(method, args); // 使用模拟对象或默认值填充args
        try {
            method.invoke(args.toArray(new Object[0]));
        }catch(Exception e) {
            // 这没问题,我们只是在测试委托
        }

        // 现在是验证部分
        List<Object> mockArgs = new ArrayList<>();
        try {
            mockArgs.add(verify(delegate));
            mockArgs.addAll(nCopies(args.size()-1, any()));
            method.invoke(mockArgs.toArray(new Object[0]));
        }catch (Exception e) {
            throw new IllegalStateException(e);
        }

    }

当我运行这个代码时,我得到的错误是:

    org.mockito.exceptions.misusing.InvalidUseOfMatchersException: 
    在此检测到参数匹配器的错误使用或错误放置:
    
    -> 在some.packagename.AbstractDelegateTest.testMethodDelegation(AbstractDelegateTest.java:81)
    
    你不能在验证或存根之外使用参数匹配器。
    参数匹配器的正确使用示例:
        when(mock.get(anyInt())).thenReturn(null);
        doThrow(new RuntimeException()).when(mock).someVoidMethod(anyObject());
        verify(mock).someMethod(contains("foo"))

我是不是做错了什么,或者如果你不知道确切的方法,是否无法验证方法调用?
英文:

I have a largish codebase with many decorator classes that usually delegate all but one method to a delegate object, i.e. something like this:

class WrapperThing implements Thing{
   private final Thing delegate;
   WrapperThing(Thing thing){this.delegate=thing;}

   public boolean method1(){ return delegate.method1(); }
   public String method2(int arg1, boolean arg2){ return delegate.method2(arg1, arg2); }
   // lots more methods here, all delegating to delegate
}

Now I am creating Unit Tests for these wrappers, using a Junit 5 @TestFactory, calling each of the methods on the WrapperThing, and want to verify that there was an invocation on the wrapped delegate, which is a Mockito mock.

Here's my code so far:

private void testMethodDelegation(final Method method) {
    D delegate = mock(delegateType);
    W wrapper = createWrapper(delegate);

    List<Object> args = new ArrayList<>(method.getParameterTypes().length + 1);
    args.add(wrapper);
    gatherMethodArgs(method, args); // populate args with mocks or default values
    try {
        method.invoke(args.toArray(new Object[0]));
    }catch(Exception e) {
        // this is fine, we're just testing the delegation
    }

    // now comes the verify part
    List<Object> mockArgs = new ArrayList<>();
    try {
        mockArgs.add(verify(delegate));
        mockArgs.addAll(nCopies(args.size()-1, any()));
        method.invoke(mockArgs.toArray(new Object[0]));
    }catch (Exception e) {
        throw new IllegalStateException(e);
    }

}

When I run this, the error I get is:

org.mockito.exceptions.misusing.InvalidUseOfMatchersException: 
Misplaced or misused argument matcher detected here:

-> at some.packagename.AbstractDelegateTest.testMethodDelegation(AbstractDelegateTest.java:81)

You cannot use argument matchers outside of verification or stubbing.
Examples of correct usage of argument matchers:
    when(mock.get(anyInt())).thenReturn(null);
    doThrow(new RuntimeException()).when(mock).someVoidMethod(anyObject());
    verify(mock).someMethod(contains("foo"))

Am I doing something wrong, or is it not possible to verify a method call if you don't know the exact method?

答案1

得分: 1

以下是您提供的代码的中文翻译:

问题在于我错误地调用了 method.invoke()。
我以为格式是 method.invoke([target, arg1, ... argn]),但实际上是 method.invoke(target, [arg1, ... argn])。今天过得很长,我的错。

这段代码有效:

private void testMethodDelegation(final Method method) {
    D delegate = mock(delegateType);
    W wrapper = createWrapper(delegate);

    List<Object> args = new ArrayList<>(method.getParameterTypes().length);
    gatherMethodArgs(method, args); // 用模拟或默认值填充 args
    try {
        method.invoke(wrapper, args.toArray(new Object[0]));
    } catch (Exception e) {
        // 这没问题,我们只是测试委托
        throw new IllegalStateException(e);
    }
    callVerify(method, delegate);
}

private void callVerify(final Method method, final D delegate) {
    // 现在是验证部分
    List<Object> mockArgs = new ArrayList<>(method.getParameterTypes().length);
    try {
        D verifyDelegate = verify(delegate);
        gatherVerifyArgs(method, mockArgs);
        method.invoke(verifyDelegate, mockArgs.toArray(new Object[0]));
    } catch (Exception e) {
        throw new IllegalStateException(e);
    }
}

private void gatherVerifyArgs(final Method method, final List<Object> args) {
    for (Class<?> parameterType : method.getParameterTypes()) {
        if (int.class == parameterType) {
            args.add(anyInt());
        } else if (boolean.class == parameterType) {
            args.add(anyBoolean());
        } else if (long.class == parameterType) {
            args.add(anyLong());
        } else if (double.class == parameterType) {
            args.add(anyDouble());
        } else if (float.class == parameterType) {
            args.add(anyFloat());
        } else if (String.class == parameterType) {
            args.add(anyString());
        } else {
            args.add(any());
        }
    }
}

private void gatherMethodArgs(final Method method, final List<Object> args) {

    int i = 0;
    for (Class<?> type : method.getParameterTypes()) {
        try {
            if (type == String.class) {
                args.add("");
            } else if (type.isArray()) {
                args.add(Array.newInstance(type.getComponentType(), 0));
            } else if (Primitives.allPrimitiveTypes().contains(type)) {
                args.add(Defaults.defaultValue(type));
            } else if (Primitives.allWrapperTypes().contains(type)) {
                args.add(Defaults.defaultValue(Primitives.unwrap(type)));
            } else if (type == List.class) {
                args.add(ImmutableList.of());
            } else if (type == Set.class) {
                args.add(ImmutableSet.of());
            } else if (type == Map.class) {
                args.add(ImmutableMap.of());
            } else if (type.getName().startsWith("java.util.")) {
                args.add(type.newInstance());
            } else {
                args.add(mock(type));
            }
        } catch (Exception e) {
            throw new IllegalStateException(
                String.format("在方法 %s 的参数 %d (%s) 模拟时出错", method, i, method.getGenericParameterTypes()[i]), e);
        }
        i++;
    }
}

如果您需要任何进一步的帮助,请告诉我。

英文:

The problem is that I was calling method.invoke() wrong.
I thought the format was method.invoke([target, arg1, ... argn]), but it is actually method.invoke(target, [arg1, ... argn]). It's been a long day, my bad.

This code works:

private void testMethodDelegation(final Method method) {
D delegate = mock(delegateType);
W wrapper = createWrapper(delegate);
List&lt;Object&gt; args = new ArrayList&lt;&gt;(method.getParameterTypes().length);
gatherMethodArgs(method, args); // populate args with mocks or default values
try {
method.invoke(wrapper, args.toArray(new Object[0]));
} catch (Exception e) {
// this is fine, we&#39;re just testing the delegation
throw new IllegalStateException(e);
}
callVerify(method, delegate);
}
private void callVerify(final Method method, final D delegate) {
// now comes the verify part
List&lt;Object&gt; mockArgs = new ArrayList&lt;&gt;(method.getParameterTypes().length);
try {
D verifyDelegate = verify(delegate);
gatherVerifyArgs(method, mockArgs);
method.invoke(verifyDelegate, mockArgs.toArray(new Object[0]));
} catch (Exception e) {
throw new IllegalStateException(e);
}
}
private void gatherVerifyArgs(final Method method, final List&lt;Object&gt; args) {
for (Class&lt;?&gt; parameterType : method.getParameterTypes()) {
if (int.class == parameterType) {
args.add(anyInt());
} else if (boolean.class == parameterType) {
args.add(anyBoolean());
} else if (long.class == parameterType) {
args.add(anyLong());
} else if (double.class == parameterType) {
args.add(anyDouble());
} else if (float.class == parameterType) {
args.add(anyFloat());
} else if (String.class == parameterType) {
args.add(anyString());
} else {
args.add(any());
}
}
}
private void gatherMethodArgs(final Method method, final List&lt;Object&gt; args) {
int i = 0;
for (Class&lt;?&gt; type : method.getParameterTypes()) {
try {
if (type == String.class) {
args.add(&quot;&quot;);
} else if (type.isArray()) {
args.add(Array.newInstance(type.getComponentType(), 0));
} else if (Primitives.allPrimitiveTypes().contains(type)) {
args.add(Defaults.defaultValue(type));
} else if (Primitives.allWrapperTypes().contains(type)) {
args.add(Defaults.defaultValue(Primitives.unwrap(type)));
} else if (type == List.class) {
args.add(ImmutableList.of());
} else if (type == Set.class) {
args.add(ImmutableSet.of());
} else if (type == Map.class) {
args.add(ImmutableMap.of());
} else if (type.getName().startsWith(&quot;java.util.&quot;)) {
args.add(type.newInstance());
} else {
args.add(mock(type));
}
} catch (Exception e) {
throw new IllegalStateException(
String.format(&quot;Error mocking parameter %d (%s) of method %s&quot;, i,
method.getGenericParameterTypes()[i], method), e);
}
i++;
}
}

huangapple
  • 本文由 发表于 2020年8月6日 09:51:19
  • 转载请务必保留本文链接:https://go.coder-hub.com/63275759.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定