Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is Mockito behaving weird with InputStreams?

While debugging I came across something incredibly strange using Mockito 1.10. I was hoping someone could explain the behavior perceived here:

When I run the following, my thread hangs and my test never returns. CPU of the Java process created goes astronomical too!

@Test(expected = IOException.class)
public void mockitoWeirdness() throws IOException {
    final InputStream mis = mock(InputStream.class);
    doThrow(IOException.class).when(mis).read();
    ByteStreams.copy(mis, new ByteArrayOutputStream());
}

When I manually stub this method as follows, the expected IOException is thrown:

@Test(expected = IOException.class)
public void nonMockitoExpected() throws IOException {
    final InputStream mis = new InputStream() {

        @Override
        public int read() throws IOException {
            throw new IOException();
        }
    };
    ByteStreams.copy(mis, new ByteArrayOutputStream());
}

Any help understanding how and why the mockito method is failing would be fantastic.

like image 442
Master_Yoda Avatar asked Sep 16 '25 14:09

Master_Yoda


2 Answers

If you take a look at the ByteStreams implementation, you can see that the read(buf) method is used. In your case it returns null because there is no mock definition for it and this causes an endless loop in the copy method.

You may either change the default mock behaviour or manually add a definition for the read(buff) method.

like image 146
Alexander Avatar answered Sep 19 '25 03:09

Alexander


You'll want to set up your mock to call the real methods of InputStream when you haven't stubbed them

final InputStream mis = Mockito.mock(InputStream.class, Mockito.CALLS_REAL_METHODS);

The javadoc states

This implementation can be helpful when working with legacy code. When this implementation is used, unstubbed methods will delegate to the real implementation. This is a way to create a partial mock object that calls real methods by default.

Mockito, by default, mocks everything. The ByteStreams#copy method you used first invokes InputStream#read(byte[]). Since mockito has mocked it, it will return 0 which ByteStreams#copy interprets as "there is more to read from this stream" and keeps reading (infinite loop).

By using Mockito.CALLS_REAL_METHODS, you're telling Mockito to call the actual implementation in InputStream, which will delegate to read(), which you've stubbed to throw an exception.

like image 43
Sotirios Delimanolis Avatar answered Sep 19 '25 04:09

Sotirios Delimanolis