3

Found very interesting issue and after debugging found the scenario to reproduce it.

So, if I have a class with package scope B that has some public method and public class A that extends it:

package somepackage;

class B {
   public void someMethod() {
      throw NullPointerException();
   }
}

package somepackage;
public class A extends B {

}

and then in the test:

A a = mock(A.class);
a.someMethod();

and guess what, I am getting NullPointerException that I just threw, so Mockito somehow creates a "real" object and calls a real method instead of mocked one. Why so?

java.lang.IllegalArgumentException
    at test.B.setProxy(B.java:6)
    at test.A.setProxy(A.java:1)
    at secretservice.service.TestFDSServiceImpl.testService(TestFDSServiceImpl.java:17)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    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.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.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)

In the example above I have IllegalArgumentException as I changed it in class B just to make sure NullPointerException is not caused by something else.

Eclipse: Juno Service Release 1, build id: 20120920-0800 Mockito: 1.8.4 (also tested with 1.9.5) JUnit: 4.10

Maciej Kowalski
  • 25,605
  • 12
  • 54
  • 63
potomok
  • 1,249
  • 3
  • 12
  • 16

1 Answers1

7

edit

Latest mockito 2.x beta use now ByteBuddy which isn't affected by this issue. There may be some API ajustements left but it is pretty much both working and functional. Also there will be breaking compatibility with existing mockito matchers. If that's ok for the project that would be great to have feedback on the API while mockito 2 is in beta.

original

There's a known issue when the mocked class have a non public parent. The method cannot be stubbed. See issue 212.

The issue is that bridge methods are generated by the compiler in order to access the method from the parent, however this confuses bytecode tools like CGLIB. Unless you can fix CGLIB, there's no real solution.

Sorry you have to workaround this in a different way :/

bric3
  • 40,072
  • 9
  • 91
  • 111
  • Thanks for this, yeap, I decided to go for very dumb solution, just creating one more class that inherits from the public class and overrides all not implemented methods, fortunately, in my case they are just setters – potomok Dec 02 '13 at 16:11