2013-11-08 4 views
0

Я использую PowerMock (Mockito), чтобы высмеять подканал другому методу в том же классе. Более конкретно у меня есть что-то вроде этого:Mockito не выбрасывает правильное исключение при использовании doThrow на шпионах

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

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

Теперь в моих модульных тестов, я могу издеваться ответы MyMethod2 с помощью шпиона, и делать что-то вроде doReturn(1).when(myClass).myMethod2(). Однако, что-то странное случается, когда я делаю что-то вроде этого: doThrow(myExeptionType).when(myClass).myMethod2(). Когда я вызываю myClass.myMethod1() во время моего теста, он выдает исключение NullPointerException, но странный бит заключается в том, что если я использую отладчик и проверяю throw e, e является правильным исключением типа MyExceptionType.

вот трассировки стека этого 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) 

Надеюсь мой вопрос не слишком запутанным, спасибо!

+2

Я немного смущен.Когда вы говорите, что вы делаете что-то вроде 'doThrow (myExeptionType)', вы имеете в виду, что вы создали экземпляр переменной myExeptionType? Если да, то какова его ценность? Или вы имеете в виду, что вы написали «MyExceptionType.class»? Не могли бы вы правильно опубликовать код, который вызывает у вас проблему, поэтому мы можем воспроизвести его; а не просто его описать? Благодарю. –

+0

Я также считаю, что ваше использование в верхнем и нижнем регистре сбивает с толку. Вы написали как 'myMethod2', так и' MyMethod2' для того же метода, что говорит о том, что то, что вы отправили, не является фактическим кодом, который дает вам проблему. Вы также использовали нижний регистр для 'myClass'. Возможно, это не актуально, но нестандартное использование верхнего и нижнего регистра делает ваш пример более сложным. –

+0

Кроме того, ваша трассировка стека предполагает, что вы используете правило ExpectedException. Вы не показали нам этого. Можете ли вы сделать это, пожалуйста? Это может повлиять на то, что происходит здесь. –

ответ

3

Ваши комментарии и последующий ответ выявили проблему. Вы пытаетесь высмеять свой объект исключения. Мокито не был предназначен для этого. Причина в том, что исключения обычно считаются объектами ценности. Они несут информацию - сообщение, трассировку стека, иногда ссылку на второе исключение; но, как правило, у них фактически нет никакой функциональности.

Целью издевательств над любым классом является получение объекта, который не имеет никаких собственных функций, то есть ни один из его методов ничего не делает, кроме случаев, когда они явно реализованы в тесте. Но исключение уже соответствует этому критерию, поэтому нет ничего, что можно было бы получить, издеваясь над этим. Совет в http://www.mockobjects.com/2007/04/test-smell-everything-is-mocked.html - хороший совет.

Итак, у вас есть несколько вариантов, которые помогут решить вашу проблему.

(1) Создайте реальное исключение и используйте его в своем тесте. В зависимости от того, какие конструкторы MyException, это может выглядеть так.

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

(2) Пусть Mockito создать объект исключения для вас, просто указав на его класс в doThrow вызова.

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

Не забудьте подтвердить свое исключение в методе тестирования. Как с помощью JUnit аннотации:

@Test(expected = MyExceptionType.class) 
2

Я обнаружил, что этот вопрос проживал в том, что Mockito пытается отфильтровать трассировку стеки, за исключение брошенного, чтобы удалить «EnhancedByMockito» строку добавляемых к издевались имена классов , Поэтому в основном я делаю это:

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

Конечно, в этом примере, mockedException.getStackTrace() возвратит нуль, который будет генерировать исключения нулевого указателя при Mockito пытался фильтровать трассировки стека.

Надеюсь, это разъяснит мой вопрос и может оказаться полезным для кого-то еще.

Чтобы решить эту проблему, я просто издевался трассировку стека для моего исключения так:

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

О, так вы говорите, что проблема в том, что вы издевались над типом исключения. Почему ты бы так поступил? Разумеется, лучшим ответом будет «не издеваться над типом исключения»? –

+0

Я думаю, что это скорее «философский» вопрос о подходе, так как в конце концов оба работают. Я хочу, чтобы модуль тестировал этот метод и только этот метод и высмеивал ЛЮБЫЕ зависимости. Может быть, я слишком строг в отношении того, как я подхожу к модульным тестам? – Nepoxx

+0

Хорошо, если есть прецедент для этого, я мог бы убедить команду Mockito добавить метод doMockAndThrow или что-то подобное, которое также заботится о насмешке трассировки стека. Так что вы должны писать 'doMockAndThrow (ExceptionType.class) .when (someMock) .someMethod()' Но мне кажется, что классы исключений являются классами «значение» - они хранят некоторые данные, но обычно они не имеют функциональности и, следовательно, издеваясь над ними, ничего не получает, и идет против одного из основных принципов насмешки. –

Смежные вопросы