How to get body as String from ServerResponse for test?

Viewed 5865

Say we have an instance of o.s.w.reactive.function.server.ServerResponse.

What is the proper way to fetch the contents of its body, in other words how to implement fetchBodyAsString function?

test(){
  ServerResponse response = getResponseFromService("mock data");

  String body = fetchBodyAsString(response);

  assertEquals("hello", body);
}

Could you also elaborate a bit on why does ServerResponse have methods for everything (cookies(), headers(), statusCode()), but the response body? I guess there should be a way to get the body with writeTo() method, although it is absolutely vague how to use it.

3 Answers

I was digging around for something similar for unit testing purposes, and stitched together the below code. It's in Kotlin, but should be relatively easy to translate to Java and solve your problem (though it definitely does seem a bit hacky).

    fun fetchBodyAsString(serverResponse: ServerResponse): String {
        val DEFAULT_CONTEXT: ServerResponse.Context = object : ServerResponse.Context {
            override fun messageWriters(): List<HttpMessageWriter<*>> {
                return HandlerStrategies.withDefaults().messageWriters()
            }

            override fun viewResolvers(): List<ViewResolver> {
                return Collections.emptyList()
            }
        }

        // Only way I could figure out how to get the ServerResponse body was to have it write to an exchange
        val request = MockServerHttpRequest.get("http://thisdoenstmatter.com").build()
        val exchange = MockServerWebExchange.from(request)
        serverResponse.writeTo(exchange, DEFAULT_CONTEXT).block()
        val response = exchange.response
        return response.bodyAsString.block()!!
    }

Basically needed to create a fake MockServerWebExchange and have the ServerResponse write to it to translate it into a MockServerHttpResponse of which you can pull the response body out of fairly painlessly. This is definitely not elegant, but it works.

Also note, I didn't test the above function itself, just that it compiles. It should work though as the function's inner code is exactly what we're using.

As for your other questions about ServerResponse, I don't know the answers, but am curious about that as well!

As far as i know ServerResponse is used at the controller or router function. For testing you can use WebTestClient

@Autowired
WebTestClient webTestClient;


@Test
void test() {

    webTestClient.get()
            .exchange()
            .expectStatus()
            .isOk()
            .expectHeader()
            .contentType(MediaType.APPLICATION_JSON)
            .expectBody()
            .jsonPath("data.name").isEqualTo("name");


}

or

@Autowired
WebTestClient webTestClient;


@Test
void test() {

    FluxExchangeResult<String> result = webTestClient.get()
            .exchange()
            .returnResult(String.class);

    int rawStatusCode = result.getRawStatusCode();
    HttpStatus status = result.getStatus();
    HttpHeaders responseHeaders = result.getResponseHeaders();
    String stringResponseBody = result.getResponseBody().blockFirst();

}

This is based on Alan Yeung solution above, except in Java. There has to be a better 'native' way to do this without loading application context.

public class ServerResponseExtractor {

  public static <T> T serverResponseAsObject(ServerResponse serverResponse,
      ObjectMapper mapper, Class<T> type) {
    String response = serverResponseAsString(serverResponse);
    try {
      return mapper.readValue(response, type);
    } catch (JsonProcessingException e) {
      throw new RuntimeException(e);
    }
  }

  public static String serverResponseAsString(ServerResponse serverResponse) {
    MockServerWebExchange exchange = MockServerWebExchange.from(
            MockServerHttpRequest.get("/foo/foo"));

    DebugServerContext debugServerContext = new DebugServerContext();
    serverResponse.writeTo(exchange, debugServerContext).block();

    MockServerHttpResponse response = exchange.getResponse();
    return response.getBodyAsString().block();

  }

  private static class DebugServerContext implements ServerResponse.Context {
    @Override
    public List<HttpMessageWriter<?>> messageWriters() {
      return HandlerStrategies.withDefaults().messageWriters();
    }
    @Override
    public List<ViewResolver> viewResolvers() {
      return Collections.emptyList();
    }
  }

}
Related