Java 对间谍使用 doThrow 时,Mockito 不会抛出正确的异常

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/19869247/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-12 20:54:00  来源:igfitidea点击:

Mockito doesn't throw correct exception when using doThrow on a spy

javajunitmockitojunit4powermock

提问by Nepoxx

I'm using PowerMock (Mockito) to mock a subcall to another method in the same class. More specifically I have something like this:

我正在使用 PowerMock (Mockito) 来模拟对同一类中另一个方法的子调用。更具体地说,我有这样的事情:

public class myClass{
    public void MyMethod1(){
        //do something
        try{
            myMethod2();
        } catch (MyExceptionType e) {
            //do something
            throw e;
        }
    }

    public int MyMethod2() throws MyExceptionType {...}
}

Now in my unit tests, I'm able to mock responses of MyMethod2 using a spy, and doing something like doReturn(1).when(myClass).myMethod2(). However, something strange happens when I do something like this: doThrow(myExeptionType).when(myClass).myMethod2(). When I call myClass.myMethod1() during my test, it throws a NullPointerException, but the strange bit is that if I use a debugger and inspect throw e, e is the correct exception of type MyExceptionType.

现在在我的单元测试中,我可以使用间谍模拟 MyMethod2 的响应,并执行类似doReturn(1).when(myClass).myMethod2(). 然而,当我这样做奇怪的事情发生了:doThrow(myExeptionType).when(myClass).myMethod2()。当我在测试期间调用 myClass.myMethod1() 时,它会抛出 NullPointerException,但奇怪的是,如果我使用调试器并检查throw e,e 是 MyExceptionType 类型的正确异常。

Here's the stack trace of that NullPointerException:

这是 NullPointerException 的堆栈跟踪:

java.lang.NullPointerException
    at java.util.Arrays$ArrayList.<init>(Arrays.java:2842)
    at java.util.Arrays.asList(Arrays.java:2828)
    at org.mockito.internal.exceptions.stacktrace.StackTraceFilter.filter(StackTraceFilter.java:31)
    at org.mockito.internal.exceptions.stacktrace.ConditionalStackTraceFilter.filter(ConditionalStackTraceFilter.java:23)
    at org.mockito.internal.invocation.realmethod.FilteredCGLIBProxyRealMethod.invoke(FilteredCGLIBProxyRealMethod.java:29)
    at org.mockito.internal.invocation.InvocationImpl.callRealMethod(InvocationImpl.java:108)
    at org.mockito.internal.stubbing.answers.CallsRealMethods.answer(CallsRealMethods.java:36)
    at org.mockito.internal.handler.MockHandlerImpl.handle(MockHandlerImpl.java:93)
    at org.mockito.internal.handler.NullResultGuardian.handle(NullResultGuardian.java:29)
    at org.mockito.internal.handler.InvocationNotifierHandler.handle(InvocationNotifierHandler.java:38)
    at org.mockito.internal.creation.MethodInterceptorFilter.intercept(MethodInterceptorFilter.java:51)
    at com.amazon.inventory.workflow.common.wrapper.FCContainerServiceWrapper$$EnhancerByMockitoWithCGLIB$$a0f00456.getContainerHierarchyDown(<generated>)
    at com.amazon.inventory.workflow.common.wrapper.containerservice.GetContainerHierarchyDownTest.runTest(GetContainerHierarchyDownTest.java:50)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at org.junit.runners.model.FrameworkMethod.runReflectiveCall(FrameworkMethod.java:45)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:42)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)
    at org.powermock.modules.junit4.rule.PowerMockStatement.evaluate(PowerMockRule.java:49)
    at org.junit.rules.ExpectedException$ExpectedExceptionStatement.evaluate(ExpectedException.java:110)
    at org.junit.rules.RunRules.evaluate(RunRules.java:18)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:263)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:68)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:47)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:231)
    at org.junit.runners.ParentRunner.schedule(ParentRunner.java:60)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:229)
    at org.junit.runners.ParentRunner.access
MyException toThrow = new MyException("testing");
doThrow(toThrow).when(someMock).someMethod();
0(ParentRunner.java:50) at org.junit.runners.ParentRunner.evaluate(ParentRunner.java:222) at org.junit.runners.ParentRunner.run(ParentRunner.java:300) at org.junit.runners.Suite.runChild(Suite.java:128) at org.junit.runners.Suite.runChild(Suite.java:24) at org.junit.runners.ParentRunner.run(ParentRunner.java:231) at org.junit.runners.ParentRunner.schedule(ParentRunner.java:60) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:229) at org.junit.runners.ParentRunner.access
doThrow(MyException.class).when(someMock).someMethod();
0(ParentRunner.java:50) at org.junit.runners.ParentRunner.evaluate(ParentRunner.java:222) at org.junit.runners.ParentRunner.run(ParentRunner.java:300) at org.junit.runner.JUnitCore.run(JUnitCore.java:148) at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:77) at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:195) at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:63) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:606) at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120)

Hopefully my question is not too confusing, thanks!

希望我的问题不要太混乱,谢谢!

采纳答案by Dawood ibn Kareem

Your comments and subsequent answer have revealed the problem. You're trying to mock your exception object. Mockito was not designed to be able to do this. The reason is that exceptions are generally considered to be value objects. They carry around information - a message, a stack trace, sometimes a reference to a second exception; but as a general rule, they don't actually have any functionality.

你的评论和随后的回答已经暴露了这个问题。您正在尝试模拟您的异常对象。Mockito 的设计初衷并非如此。原因是异常通常被认为是值对象。它们携带信息——一条消息、一个堆栈跟踪,有时是对第二个异常的引用;但作为一般规则,它们实际上没有任何功能。

The purpose of mocking any class is to get an object that has none of its own functionality, that is, none of its methods do anything, except where explicitly implemented within the test. But an exception already fits that criterion, so there is nothing to be gained by mocking it. The advice at http://www.mockobjects.com/2007/04/test-smell-everything-is-mocked.htmlis good advice indeed.

模拟任何类的目的是获得一个没有自己功能的对象,也就是说,它的任何方法都不做任何事情,除非在测试中明确实现。但是一个例外已经符合这个标准,所以嘲笑它没有任何好处。在该建议http://www.mockobjects.com/2007/04/test-smell-everything-is-mocked.html是很好的建议确实如此。

So, you have a couple of options, both of which will solve your problem nicely.

因此,您有多种选择,这两种选择都可以很好地解决您的问题。

(1) Create a real exception and use that in your test. Depending on what constructors MyExceptionhas, this might look like this.

(1) 创建一个真正的异常并在您的测试中使用它。根据构造函数的不同MyException,这可能看起来像这样。

@Test(expected = MyExceptionType.class)

(2) Let Mockito create the exception object for you, by just specifying its class in the doThrowcall.

(2) 让 Mockito 为您创建异常对象,只需在doThrow调用中指定其类即可。

MyClass mySpy = Mockito.spy(MyClass.class);
MyException mockedException = Mockito.mock(MyException.class);
doThrow(mockedException).when(mySpy).someMethod();

回答by javaPlease42

Remember to assert your exception in your test method. Like by using the JUnit annotation:

请记住在您的测试方法中声明您的异常。就像使用 JUnit 注释一样:

throwableException = (Exception) mock(Class.forName(exceptionToThrow));
StackTraceElement[] mockedStackTrace = new StackTraceElement[0];
when(throwableException.getStackTrace()).thenReturn(mockedStackTrace);

回答by Nepoxx

I've found that the issue resided in the fact that mockito tries to filter out the stack trace of the exception thrown to remove the "EnhancedByMockito" strings appended to mocked class names. So basically I was doing this:

我发现问题在于 mockito 试图过滤掉抛出的异常的堆栈跟踪,以删除附加到模拟类名的“EnhancedByMockito”字符串。所以基本上我是这样做的:

##代码##

Of course, in this example, mockedException.getStackTrace()would return null, which would then generate a null pointer exception when Mockito tried to filter the stack trace.

当然,在这个例子中,mockedException.getStackTrace()会返回 null,然后当 Mockito 尝试过滤堆栈跟踪时会产生一个空指针异常。

Hopefully this clarifies my question and could end up being useful to someone else.

希望这能澄清我的问题,并最终对其他人有用。

To solve the issue, I simply mocked a stack trace for my exception like so:

为了解决这个问题,我简单地模拟了我的异常的堆栈跟踪,如下所示:

##代码##