3

I am doing something similar to mentioned in Example of using StreamingOutput as Response entity in Jersey

@GET
@Produces(MediaType.APPLICATION_OCTET_STREAM)
public Response streamExample(@Context UriInfo uriInfo) {
  StreamingOutput stream = new StreamingOutput() {
    @Override
    public void write(OutputStream os) throws IOException,WebApplicationException {
    try{
      Writer writer = new BufferedWriter(new OutputStreamWriter(os));
      //Read resource from jar
      InputStream inputStream = getClass().getClassLoader().getResourceAsStream("public/" + uriInfo.getPath());

      ...//manipulate the inputstream and build string with StringBuilder here//.......
      String inputData = builder.toString();
      Writer writer = new BufferedWriter(new OutputStreamWriter(os));
      writer.write(inputData);
      writer.flush();
    } catch (ExceptionE1) {
        throw new WebApplicationException();
      }
    }
};
  return Response.ok(stream,MediaType.APPLICATION_OCTET_STREAM).build();
}

I am trying to unit test this by mocking URIInfo like mentioned in How to get instance of javax.ws.rs.core.UriInfo

  public void testStreamExample() throws IOException, URISyntaxException {
        UriInfo mockUriInfo = mock(UriInfo.class);
        Mockito.when(mockUriInfo.getPath()).thenReturn("unusal-path");
        Response response = myresource.streamExample(mockUriInfo);}

I want to be able to check that I get an Exception when I switch the path to jar to something else.But, when I run/debug the test, I never enter the

public void write(OutputStream os) throws IOException,
            WebApplicationException {...}

part and I only always hit the return Response.ok(stream,MediaType.APPLICATION_OCTET_STREAM).build();

Am I missing something very obvious here??

Community
  • 1
  • 1
daredadevil
  • 317
  • 1
  • 6
  • 19

1 Answers1

9

Because the stream is not written to until it hits the MessageBodyWriter (which is the component that ends up calling the StreamingOutput#write).

What you can do, is just get the Response from the return and call Response#getEntity() (which returns an Object) and cast it to StreamingOutput. Then call the write method yourself, passing an OutputStream, maybe a ByteArrayOutputStream so you can get the contents as a byte[] to check it. It all would look something like

UriInfo mockInfo = mockUriInfo();
Response response = resource.streamExample(mockInfo);
StreamingOutput output = (StreamingOutput) response.getEntity();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
output.write(baos);
byte[] data = baos.toByteArray();
String s = new String(data, StandardCharsets.UTF_8);
assertThat(s, is("SomeCharacterData"));
Paul Samsotha
  • 205,037
  • 37
  • 486
  • 720
  • Thanks for your reply. If I understood correctly , any check would involve me actually comparing the string derived from the output byte array and what I actually expect. I went with the idea of throwing an exception since my content is going to change from time and is quite large. I guess that's not an option now. – daredadevil Jun 16 '16 at 05:36
  • 1
    But it's a unit test. The result should never change. Just use a simple text file that it located with URI, and use the contents of the text file to test against. – Paul Samsotha Jun 16 '16 at 05:39
  • 1
    You can add a `public` directory in your test resources, and the streaming output should get the text file from there. Or even make the directory configurable through a configuration property. That's what I would do – Paul Samsotha Jun 16 '16 at 05:41
  • 2
    There's a small error to this code, replace `output.toByteArray();` with `baos.toByteArray();` – fusion Jan 18 '18 at 10:00