VB_
VB_

Reputation: 45692

Mock OutputStream#flush method

I've got: I have method void method of service class. This method takes some data from remote and than flush them with OutputStream.

public void pullAndFlushData(URI uri, Params params) {
  InputStream input = doHttpRequest(uri, params);
  OutputStream output = new OutputStream("somepath");
  IOUtils.copyLarge(input, output);
  output.flush();
  output.close();
}

I want: To test the results of this method. So I want to mock output.flush() and check whether it contains correct data.

Question: How to mock OutputStream#flush method?

Upvotes: 2

Views: 2486

Answers (1)

slim
slim

Reputation: 41223

Your current code won't work:

 OutputStream output = new OutputStream("SomePath");

... won't compile because OutputStream is abstract.

So somewhere you're going to need to tell the method what OutputStream to use. To make it more testable, make the stream a parameter.

 public void pullAndFlushData(OutputStream output, URI uri, Params params) {
    InputStream input = doHttpRequest(uri, params);
    IOUtils.copyLarge(input, output);
    output.flush();
    output.close();
 }

Alternatively, output could be a field in the object, populated by the constructor or a setter. Or you could pass the object a factory. Whichever of these you choose, it means that the caller can take control of what kind of OutputStream is used -- for the production code, a FileOutputStream; for tests, a ByteArrayOutputStream.

You may wish to review the decision to close() the OutputStream here - and instead do it in the same block as the OutputStream is opened.

Now you can test it by having your unit test supply an OutputStream.

@Test
public void testPullAndFlushData() {
     URI uri = ...;
     Params params = ...;
     ByteArrayOutputStream baos = new ByteArrayOutputStream();
     someObject.pullAndFlushData(baos, uri, params);
     assertSomething(..., baos.toByteArray());
}

This doesn't use Mockito, but it's a good pattern for testing methods that use OutputStream.

You could let Mockito mock an OutputStream and use it in the same way - setting expectations for the write() calls upon it. But that would become quite brittle about the way copyLarge() chunks the data.

You could also use Mockito's spy() to check that calls were made to your real ByteArrayOutputStream.

@Test
public void testPullAndFlushData() {
     URI uri = ...;
     Params params = ...;
     ByteArrayOutputStream spybaos = spy(new ByteArrayOutputStream());
     someObject.pullAndFlushData(spybaos, uri, params);
     assertSomething(..., spybaos.toByteArray());
     verify(spybaos).flush(); // asserts that flush() has been called.
}

However, note that the Mockito team was quite reluctant to provide spy(), and in most cases doesn't believe it's a good way to test. Read the Mockito docs for reasons.

Upvotes: 3

Related Questions