Search code examples
javaunit-testingmockitohystrix

Mockito throws NPE when stubbing method from base class


I use a Hystrix (v. 1.5.6) command in my tested class, and I'm trying to mock it with Mockito (v. 1.10.19).

Recently I've had to add a special behavior if the command failed because of a timeout:

try {
    result = command.execute();
} catch (HystrixRuntimeException err) {
    if (command.isResponseTimedOut()) {
        /** do stuff **/
    }
    /** do stuff **/;
}

This is the code in my test class :

HttpClientCommand command = mock(HttpClientCommand.class);

when(commandFactory.get(anyString(), anyString(), anyString(), anyMapOf(String.class, String.class))).thenReturn(command);
when(command.execute()).thenThrow(hystrixRuntimeException);
when(command.isResponseTimedOut()).thenReturn(false);

My HttpClientCommand extends the HystrixCommand class. The execute method is in this class, and I have no problem stubbing it.

The HystrixCommand class then extends the AbstractCommand class, where the isResponseTimedOut method is. When I try to stub it, I get a NPE from Mockito (on the when(command.isResponseTimedOut()).thenReturn(false); line, before calling any method on my tested class).

Stack trace :

java.lang.Exception: Unexpected exception, expected<my.custom.exception> but was<java.lang.NullPointerException>

    at org.junit.internal.runners.statements.ExpectException.evaluate(ExpectException.java:28)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.mockito.internal.runners.JUnit45AndHigherRunnerImpl.run(JUnit45AndHigherRunnerImpl.java:37)
    at org.mockito.runners.MockitoJUnitRunner.run(MockitoJUnitRunner.java:62)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:117)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:42)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:262)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:84)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:497)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)
Caused by: java.lang.NullPointerException
    at com.netflix.hystrix.AbstractCommand.isResponseTimedOut(AbstractCommand.java:1809)
    at com.netflix.hystrix.HystrixCommand.isResponseTimedOut(HystrixCommand.java:46)
    at com.myClassTest.testGetLocation_shouldThrowCustomException_whenRequestException(myClassTest.java:300)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:497)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.junit.internal.runners.statements.ExpectException.evaluate(ExpectException.java:19)
    ... 22 more

How can I stub this method?


Solution

  • OK the issue is due to a bug with CGLIB, mockito 1.x uses CGLIB. When the method to mock is declared in a non public parent, then CGLIB doesn't intercept the method, hence it executes the real code. The issue is still available on our old issue tracker : issue 212

    Two options :

    1. Write a public overload in HttpHystrixCommand that will invoke the super method, but since the abstract command have a lot of methods to handle failure, this is probalby not an option that will scale very well.

    2. Use mockito 2. We have worked with the developer of ByteBuddy – Rafel Winterhalter – to replace CGLIB. ByteBuddy does not have the same shortcomings of CGLIB which was almost abandonned for years. Current version is 2.2.29 and at the time of this anwer regular updates are released.