How to Mock StreamingResponseBody in Testing?

5.3k Views Asked by At

In my production code I need to execute POST command to a controller which response StreamingResponseBody. A short example of such code is :

@RestController
@RequestMapping("/api")
public class DalaLakeRealController {

    @PostMapping(value = "/downloaddbcsv", produces = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<StreamingResponseBody> downloadDBcsv(@Valid @RequestBody SearchQuery searchRequest) {
        return ResponseEntity.ok()
                .header(HttpHeaders.CONTENT_TYPE, "application/csv")
                .header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=demoStream.csv")
                .body(
                        get3Lines()
                );
    }

    public StreamingResponseBody get3Lines() {
        StreamingResponseBody streamingResponseBody = new StreamingResponseBody() {
            @Override
            public void writeTo(OutputStream outputStream) throws IOException {
                outputStream.write("LineNumber111111111\n".getBytes());
                outputStream.write("LineNumber222222222\n".getBytes());
                outputStream.write("LineNumber333333333\n".getBytes());
            }
        };
        return streamingResponseBody;
    }
}

In the testing I would like to mock the response from this controller. I have read the following link : Using MockRestServiceServer to Test a REST Client to mock external controllers.
But in andRespond of mockServer it expects ClientHttpResponse or ResponseCreator

mockServer.expect(once(), requestTo("http://localhost:8080/api/downloaddbcsv"))
    .andRespond(withSuccess("{message : 'under construction'}", MediaType.APPLICATION_JSON));

How do I respond with StreamingResponseBody in MockRestServiceServer?

2

There are 2 best solutions below

0
jpllosa On

I hope this will guide you...

@Test
public void whenTopicNotExistsThenTopicNotFound() throws IOException, NakadiException {
    when(eventTypeRepository.findByName(TEST_EVENT_TYPE_NAME)).thenThrow(NoSuchEventTypeException.class);

    final StreamingResponseBody responseBody = createStreamingResponseBody();

    final Problem expectedProblem = Problem.valueOf(NOT_FOUND, "topic not found");
    assertThat(responseToString(responseBody), TestUtils.JSON_TEST_HELPER.matchesObject(expectedProblem));
}

Got the above code from: https://www.programcreek.com/java-api-examples/index.php?api=org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody

The idea is to convert the StreamingResponseBody into a String. Then you can do something like:

mockServer.expect(once(), requestTo("http://localhost:8080/api/downloaddbcsv"))
.andRespond(withSuccess(responseToString(responseBody), MediaType.APPLICATION_JSON));

Furthermore, take a look here: https://github.com/spring-projects/spring-framework/blob/master/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/StreamingResponseBodyReturnValueHandlerTests.java

The streamingResponseBody() and responseEntity() test methods convert the response to aString.

0
Eli On

Although this post is old, this answer might help someone looking for this nowdays.

The answer for the question is the Functional Interface (streamingResponseBody) shown in the code below combined with the mock return of the download in the cloudStorageService.

    @Test
    @WithUserDetailsMock(
        email = "participant@localhost",
        authorities = AuthoritiesConstants.USER,
        cashflowAuthorities = CashflowAuthoritiesConstants.PARTICIPANT
    )
    void testDownloadFileThenSuccess() throws Exception {
        Member loggedMember = memberService.getLoggedMember();
        File file = EntityBuilder.file(loggedMember.getGroup()).build(fileRepository::save);
        SubmittedPayment submittedPayment = EntityBuilder
            .submittedPayment(loggedMember)
            .with(SubmittedPayment::setFile, file)
            .build(submittedPaymentRepository::save);

        String endpoint = String.format("/api/payment/submit/%d/file", submittedPayment.getId());
        byte[] fileBytes = "some content".getBytes();
        StreamingResponseBody streamingResponseBody = objectStream -> {
            var mockInputStream = new ByteArrayInputStream(fileBytes);
            try (mockInputStream) {
                byte[] bytes = mockInputStream.readAllBytes();
                objectStream.write(bytes, 0, bytes.length);
            }
        };
        doReturn(streamingResponseBody).when(cloudStorageService).download(fileArgumentCaptor.capture());

        MvcResult response = restTransactionMockMvc.perform(get(endpoint)).andExpect(status().isOk()).andReturn();

        assertThat(file).isEqualTo(fileArgumentCaptor.getValue());
        assertThat(Arrays.equals(fileBytes, response.getResponse().getContentAsByteArray())).isTrue();
    }