1

I try to mock a final method (readChar of class DataInputStream):

MyClassTest

@RunWith(PowerMockRunner.class)
@PrepareForTest(DataInputStream.class)
public class MyClassTest {

    @Test
    public void testMyMethod() throws IOException {
        DataInputStream mockStream = PowerMockito.mock(DataInputStream.class);
        Mockito.when(mockStream.readChar()).thenReturn('a');
        System.out.println(mockStream.readChar());  // OK (print 'a')
        Assert.assertEquals('a', MyClass.myMethod(mockStream));
    }
}

MyClass

public class MyClass {
    public static char myMethod(DataInputStream dis) throws IOException {
        return dis.readChar();  // NPE raises
    }
}

It works when calling the mocked method in testMyMethod() but in myMethod() NullPointerException raises, why?

EDIT :

The complete failure trace :

java.lang.NullPointerException
    at java.io.DataInputStream.readChar(Unknown Source)
    at test.test.MyClass.myMethod(MyClass.java:8)
    at test.test.MyClassTest.testMyMethod(MyClassTest.java:23)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at org.junit.internal.runners.TestMethod.invoke(TestMethod.java:59)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$PowerMockJUnit44MethodRunner.runTestMethod(PowerMockJUnit44RunnerDelegateImpl.java:310)
    at org.junit.internal.runners.MethodRoadie$2.run(MethodRoadie.java:79)
    at org.junit.internal.runners.MethodRoadie.runBeforesThenTestThenAfters(MethodRoadie.java:87)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$PowerMockJUnit44MethodRunner.executeTest(PowerMockJUnit44RunnerDelegateImpl.java:294)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$PowerMockJUnit44MethodRunner.runBeforesThenTestThenAfters(PowerMockJUnit44RunnerDelegateImpl.java:282)
    at org.junit.internal.runners.MethodRoadie.runTest(MethodRoadie.java:77)
    at org.junit.internal.runners.MethodRoadie.run(MethodRoadie.java:42)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.invokeTestMethod(PowerMockJUnit44RunnerDelegateImpl.java:207)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.runMethods(PowerMockJUnit44RunnerDelegateImpl.java:146)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$1.run(PowerMockJUnit44RunnerDelegateImpl.java:120)
    at org.junit.internal.runners.ClassRoadie.runUnprotected(ClassRoadie.java:27)
    at org.junit.internal.runners.ClassRoadie.runProtected(ClassRoadie.java:37)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.run(PowerMockJUnit44RunnerDelegateImpl.java:122)
    at org.powermock.modules.junit4.common.internal.impl.JUnit4TestSuiteChunkerImpl.run(JUnit4TestSuiteChunkerImpl.java:104)
    at org.powermock.modules.junit4.common.internal.impl.AbstractCommonPowerMockRunner.run(AbstractCommonPowerMockRunner.java:53)
    at org.powermock.modules.junit4.PowerMockRunner.run(PowerMockRunner.java:53)
    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:459)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:675)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)
Rüdiger Herrmann
  • 20,512
  • 11
  • 62
  • 79
csa
  • 1,806
  • 2
  • 12
  • 10

2 Answers2

2

First this code is a mock antipattern : Don't mock types you don't own! (see this answer on StackOverflow)

Second, DataInputStream is a class of the JDK PowerMock cannot use the same hacky classloader that modifies byte code.

For that there's a solution and two possible tricks :

  • use the interface
  • encapsulate the call in a class that you own, then prepare this class instead, documented here at google code, or even better make your own mockable class (without the need of powermock)
  • use an agent, documented here at google code

The first option is clearly the very best, and the first two options also allows one to avoid this mock antipattern.

Community
  • 1
  • 1
bric3
  • 40,072
  • 9
  • 91
  • 111
1

DataInputStream is a 'system' class from JVM which is probably already loaded by JVM. @PrepareForTest would have to remove final modifier from the methods (to be able to mock), but it can't do so for already-loaded classes (HotSpot JVM doesn't support class signature changes for already-loaded classes), and this is probably why you get this exception.

Luckily there's also DataInput interface implemented by DataInputStream - maybe you can try mocking not DataInputStream but DataInput, for this you don't even need PowerMock, just Mockito.

iirekm
  • 8,890
  • 5
  • 36
  • 46
  • I have mocked DataInput instead of DataInputStream and it works. Thanks for your clear explanation and your help ! – csa Mar 28 '15 at 15:42