MockMvc vs WebTestClient vs TestRestTemplate. — Cracked Java
// Spring Framework & Spring Boot · Testing — Unit, Slice, Integration
MidCoding

MockMvc vs WebTestClient vs TestRestTemplate.

Three clients exist because they drive your app at three different levels — a mock servlet, a fluent reactive client, and a real HTTP socket. Picking wrong means either slow tests or a fidelity gap.

MockMvc — mock servlet, no real port

MockMvc invokes the DispatcherServlet directly with mock HttpServletRequest/Response objects. No socket is opened, no port is bound, nothing is serialized over the wire. It's fast and it's the natural client for @WebMvcTest (and @SpringBootTest(webEnvironment = MOCK) with @AutoConfigureMockMvc).

mvc.perform(post("/orders").contentType(APPLICATION_JSON).content(body))
   .andExpect(status().isCreated())
   .andExpect(jsonPath("$.id").exists());

Because it's a mock, it tests your Spring MVC stack (mapping, filters, advice) but not the actual HTTP transport, connection handling, or a real client library.

TestRestTemplate — real HTTP, blocking

A lenient, test-friendly wrapper around RestTemplate that makes real HTTP calls to a running server. Use it with @SpringBootTest(webEnvironment = RANDOM_PORT): the embedded server is up, and TestRestTemplate hits it over the loopback socket.

@SpringBootTest(webEnvironment = RANDOM_PORT)
class OrderApiTest {
    @Autowired TestRestTemplate rest;
    @Test void creates() {
        var resp = rest.postForEntity("/orders", new OrderDto(), OrderDto.class);
        assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.CREATED);
    }
}

Highest fidelity (real serialization, real status line), but slowest and blocking. It won't follow redirects or throw on 4xx/5xx by default — convenient for asserting error responses.

WebTestClient — fluent, works both ways

The modern, reactive-style client with a fluent assertion API. Despite living in WebFlux, it works in two modes: bound to a real RANDOM_PORT server (real HTTP), or bound directly to MockMvc / the application context (no socket, like MockMvc but with a nicer API). That dual nature makes it the recommended client for new code, MVC or reactive.

client.post().uri("/orders").bodyValue(dto)
      .exchange()
      .expectStatus().isCreated()
      .expectBody().jsonPath("$.id").exists();

Choosing

  • MockMvc — controller slice tests; fast, no transport.
  • WebTestClient — default for new tests; fluent API; reactive apps; can run mocked or over a real port.
  • TestRestTemplate — when you specifically want a blocking, real-HTTP round trip in a full @SpringBootTest.

Mark your status