InputStream is very difficult to mock, not least because of its three read
overrides as well as the readNBytes
and readAllBytes
methods added in later Java versions. If done with Mockito, you'll need to have all of those method implementations interact with the same data, or else you'll wind up with a fragile test that may break as soon as your implementation calls different InputStream methods. If you must use a test double, you'd be better off writing a "fake", but there's no reason to do so when Java has ByteArrayInputStream built in: You can construct your byte[]
buffer (or write a helper method to construct one as your test needs) and substitute it for your InputStream in your test. This should suffice for anyone who comes to this question simply asking about mocking an InputStream in general.
final byte[] testFileData = {
0x74, 0x65, 0x73, 0x74, 0x20, 0x64, 0x61, 0x74, 0x61, 0x0d, 0x0a};
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(testFileData);
Unfortunately, this doesn't answer the question about how to specifically provide a test fixture for Azure's BlobInputStream, which is particularly tricky because BlobInputStream has a four-arg constructor with a lot of Azure internals. The good news is that since 1.9.5 Mockito provides a delegatesTo
method in AdditionalAnswers documented (with my own emphasis) as:
An answer that directly forwards the calls to the delegate. The delegate may or may not be of the same type as the mock. If the type is different, a matching method needs to be found on delegate type otherwise an exception is thrown.
That means that you can simulate a BlobInputStream by creating a real ByteArrayInputStream and delegating the overridable methods to it. Unfortunately, delegatesTo
is on AdditionalAnswers and not the Answers enum (and it requires an instance parameter anyway that you can't provide in annotations), so you'll need to construct your mock manually:
BlobInputStream mockInputStream = Mockito.mock(
BlobInputStream.class,
AdditionalAnswers.delegatesTo(byteArrayInputStream));
when(cloudBlobContainer.getBlobReferenceFromServer(anyString()))
.thenReturn(cloudBlob);
when(cloudBlob.openInputStream()).thenReturn(mockInputStream);
However, if possible, this would be a good opportunity to separate your code into the part that deals with Azure input streams versus your application code that works with any InputStream. If, for example, your code takes the BlobInputStream and runs parsing or error correction code, you can factor out a method handleInputStream(InputStream)
and pass in your own ByteArrayInputStream to test it heavily. This minimizes your need to mock a BlobInputStream, or may eliminate it entirely if you choose to only test your BlobInputStream handling as an integration test using a real backend. See also Mocking Files in Java - Mock Contents - Mockito, where Brice discusses a similar split between unit and integration tests instead of mocking a java.io.File instance.