简体   繁体   English

当对间谍使用doThrow时,Mockito不会引发正确的异常

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

I'm using PowerMock (Mockito) to mock a subcall to another method in the same class. 我正在使用PowerMock(Mockito)模拟同一类中另一个方法的子调用。 More specifically I have something like this: 更具体地说,我有这样的事情:

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() . 现在在我的单元测试中,我可以使用间谍来模拟MyMethod2的响应,并执行doReturn(1).when(myClass).myMethod2() However, something strange happens when I do something like this: doThrow(myExeptionType).when(myClass).myMethod2() . 但是,当我执行以下操作时,会发生一些奇怪的事情: 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. 当我在测试过程中调用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$1.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$3.run(ParentRunner.java:231)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:60)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:229)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:50)
    at org.junit.runners.ParentRunner$2.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$3.run(ParentRunner.java:231)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:60)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:229)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:50)
    at org.junit.runners.ParentRunner$2.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! 希望我的问题不会太混乱,谢谢!

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. Mockito并非旨在做到这一点。 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. 但通常来说,它们实际上没有任何功能。

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.html is 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. (1)创建一个真正的异常,并在测试中使用它。 Depending on what constructors MyException has, this might look like this. 根据MyException具有哪些构造函数,它可能看起来像这样。

MyException toThrow = new MyException("testing");
doThrow(toThrow).when(someMock).someMethod();

(2) Let Mockito create the exception object for you, by just specifying its class in the doThrow call. (2)让Mockito通过在doThrow调用中指定其异常类来为您创建异常对象。

doThrow(MyException.class).when(someMock).someMethod();

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. 我发现问题出在以下事实上:mockito试图过滤抛出的异常的堆栈跟踪,以删除附加到模拟的类名的“ EnhancedByMockito”字符串。 So basically I was doing this: 所以基本上我是这样做的:

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

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尝试过滤堆栈跟踪时将生成null指针异常。

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: 为了解决这个问题,我只是模拟了异常的堆栈跟踪,如下所示:

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

Remember to assert your exception in your test method. 请记住在测试方法中声明异常。 Like by using the JUnit annotation: 就像通过使用JUnit批注:

@Test(expected = MyExceptionType.class)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM