Using mockito-inline MockedConstruction to mock FileInputStream throws Could not initialize mocked construction

6.8k Views Asked by At

I'm trying to get rid of PowerMock and replace it with mockito-inline new feature Mocking object construction , as I can't refactor the old source code.

One of the test classes I have is mocking FileInputStream, the class under test FileViewer

import java.awt.Button;
import java.awt.Event;
import java.awt.Font;
import java.awt.TextArea;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

public class FileViewer extends java.awt.Frame {
    private static final long serialVersionUID = 1L;
    Button close;
    GUIListener fGuiListener;

    public FileViewer() {
        super();
        fGuiListener = new GUIListener();
        addWindowListener(fGuiListener);
    }

    public FileViewer(String filename) throws IOException {
        super("FileViewer: " + filename);
        fGuiListener = new GUIListener();
        addWindowListener(fGuiListener);
        File f = new File(filename);
        int size = (int) f.length();
        int bytes_read = 0;
        byte[] data = new byte[size];
        try (FileInputStream in = new FileInputStream(f)) {
            while (bytes_read < size)
                bytes_read += in.read(data, bytes_read, size - bytes_read);
        }
        TextArea ta = new TextArea(new String(data, 0), 24, 80);
        ta.setFont(new Font("Helvetica", Font.PLAIN, 12));
        ta.setEditable(false);
        this.add("Center", ta);
        close = new Button("Close");
        this.add("South", close);
        this.pack();
        this.show();
    }

    public boolean action(Event e, Object what) {
        if (e.target == close) {
            this.hide();
            this.dispose();
            return true;
        } else
            return false;
    }
}

and the original unit test using powermock was

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import java.io.FileInputStream;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PowerMockIgnore;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

@PowerMockIgnore({ "javax.management.*", "javax.security.*" })
@RunWith(PowerMockRunner.class)
@PrepareForTest({ FileViewer.class, FileInputStream.class, java.awt.Frame.class })

public class FileViewerTest {
    String dir = System.getProperty("user.dir");
    String fileName = "/testFiles/testRead.txt";

    FileInputStream mockStream = null;

    @Before
    public void setup() throws Exception {
        mockStream = Mockito.mock(FileInputStream.class);
        when(mockStream.read(any(byte[].class), anyInt(), anyInt())).thenReturn(2000);
        PowerMockito.whenNew(FileInputStream.class).withAnyArguments().thenReturn(mockStream);
    }

    @Test
    public void testFileViewer_wFilename() throws Exception {
        FileViewer spy = Mockito.spy(new FileViewer(dir + fileName));
        verify(mockStream, times(1)).close();
        spy.dispose();
    }

}

I tried to follow the example Mock Java Constructors With Mockito | Configuration and Examples and creat a new unit test using MockedConstruction as the following

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import java.io.File;
import java.io.FileInputStream;

import org.junit.Test;
import org.mockito.MockedConstruction;
import org.mockito.Mockito;

public class FileViewerMockitoTest {
    String dir = System.getProperty("user.dir");
    String fileName = "/testFiles/testRead.txt";

    @Test
    public void testFileViewerWithFilename() throws Exception {
        try (MockedConstruction<FileInputStream> mocked = Mockito.mockConstruction(FileInputStream.class,
                (mock, context) -> {
                    // further stubbings ...
                    when(mock.read(any(), any(), any())).thenReturn((int) new File(dir + fileName).length() + 1);
                })) {

            FileViewer cut = new FileViewer(dir + fileName);
            verify(mocked, times(1)).close();
            cut.dispose();
        }
    }

}

But I got the following Exception

org.mockito.exceptions.base.MockitoException: Could not initialize mocked construction
    at java.io.FileInputStream.<init>(FileInputStream.java)
    at sun.misc.URLClassPath$FileLoader$1.getInputStream(URLClassPath.java:1385)
    at sun.misc.Resource.cachedInputStream(Resource.java:77)
    at sun.misc.Resource.getByteBuffer(Resource.java:160)
    at java.net.URLClassLoader.defineClass(URLClassLoader.java:455)
    at java.net.URLClassLoader.access$100(URLClassLoader.java:74)
    at java.net.URLClassLoader$1.run(URLClassLoader.java:369)
    at java.net.URLClassLoader$1.run(URLClassLoader.java:363)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(URLClassLoader.java:362)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:418)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:355)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:351)
    at com.cds.nrd.xss.util.FileViewerMockitoTest.testFileViewerWithFilename(FileViewerMockitoTest.java:30)
    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:498)
    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.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.junit.runner.JUnitCore.run(JUnitCore.java:137)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:115)
    at org.junit.vintage.engine.execution.RunnerExecutor.execute(RunnerExecutor.java:43)
    at java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:184)
    at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193)
    at java.util.Iterator.forEachRemaining(Iterator.java:116)
    at java.util.Spliterators$IteratorSpliterator.forEachRemaining(Spliterators.java:1801)
    at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:482)
    at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:472)
    at java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:151)
    at java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:174)
    at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
    at java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:418)
    at org.junit.vintage.engine.VintageTestEngine.executeAllChildren(VintageTestEngine.java:82)
    at org.junit.vintage.engine.VintageTestEngine.execute(VintageTestEngine.java:73)
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:229)
    at org.junit.platform.launcher.core.DefaultLauncher.lambda$execute$6(DefaultLauncher.java:197)
    at org.junit.platform.launcher.core.DefaultLauncher.withInterceptedStreams(DefaultLauncher.java:211)
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:191)
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:137)
    at org.eclipse.jdt.internal.junit5.runner.JUnit5TestReference.run(JUnit5TestReference.java:98)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:40)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:529)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:756)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:452)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:210)
Caused by: java.lang.NullPointerException
    at com.cds.nrd.xss.util.FileViewerMockitoTest.lambda$0(FileViewerMockitoTest.java:27)
    at org.mockito.internal.creation.bytebuddy.InlineByteBuddyMockMaker$InlineConstructionMockControl.lambda$enable$0(InlineByteBuddyMockMaker.java:710)
    at org.mockito.internal.creation.bytebuddy.InlineByteBuddyMockMaker.lambda$new$3(InlineByteBuddyMockMaker.java:272)
    at org.mockito.internal.creation.bytebuddy.MockMethodAdvice.handleConstruction(MockMethodAdvice.java:176)
    at org.mockito.internal.creation.bytebuddy.inject.MockMethodDispatcher.handleConstruction(MockMethodDispatcher.java:53)
    ... 57 more

Any idea about the reason of the exception or how to make it work, or any alternate approach?

1

There are 1 best solutions below

0
On

Found the solution:

We can't set any() parameter in the stub of the lambda function in try-with-resource (in your case, when(mock.read(any(), any(), any())) in FileViewerMockitoTest), which will cause mock.read(any(), any(), any()) to throw an NPE: "could not unbox null" (don't know what it means).

Just simply replace (all of) these arguments to something like any(ArgClass.class), will solve the NPE problem, so do with the Could not initialize mocked construction MockitoException problem.

(How did I find the problem? By extracting the lambda function as a method, we can get the real description of the NPE in the debug environment.)

(I was surprised that there was no information on the internet other than this question.)