Lesson 19 / 25

@SpringBootTest and Test Slices

Choose between full-context tests and focused slices, and replace beans with @MockitoBean.

Load only what the test needs

Spring Boot tests can start the whole application context with @SpringBootTest, which is realistic but slow. Test slices load only part of it: @WebMvcTest loads the web layer (controllers, filters, JSON conversion, validation) for testing controllers with MockMvc; @DataJpaTest loads JPA components (entities, repositories, transactions, Flyway) and rolls back after each test; @JsonTest focuses on JSON serialisation; @RestClientTest tests HTTP clients against a mock server. Within a Spring test, replace a bean with a Mockito mock using @MockitoBean (introduced in Spring Framework 6.2 / Spring Boot 3.4, replacing the now-deprecated @MockBean) or wrap it with @MockitoSpyBean. Spring caches application contexts between tests with the same configuration, so keep configurations consistent; each distinct combination of mocked beans creates a new context and slows the suite. Use plain JUnit and Mockito, without Spring, for business logic: those tests run in milliseconds.

Full context versus slices

@SpringBootTest loads everything; slices load just the layer under test.

A full stack of layered blocks on the left; on the right, the same stack with only one layer highlighted and the rest faded.
Figure 7.1 — A full application context compared with a test slice.

A controller slice test with a mocked service

Only the web layer starts; the service is a Mockito mock.

@WebMvcTest(OrderController.class)
class OrderControllerTest {

    @Autowired MockMvc mvc;

    @MockitoBean                                // Spring Framework 6.2+ (replaces @MockBean)
    OrderService orderService;

    @Test
    void returnsOrderSummary() throws Exception {
        given(orderService.summary(42L))
            .willReturn(new OrderSummary(42L, "Asha", new BigDecimal("499.00"), OrderStatus.PAID));

        mvc.perform(get("/api/orders/42").accept(MediaType.APPLICATION_JSON))
           .andExpect(status().isOk())
           .andExpect(jsonPath("$.id").value(42))
           .andExpect(jsonPath("$.customerName").value("Asha"))
           .andExpect(jsonPath("$.status").value("PAID"));
    }

    @Test
    void rejectsInvalidQuantity() throws Exception {
        mvc.perform(post("/api/orders")
                .contentType(MediaType.APPLICATION_JSON)
                .content("{\"sku\":\"pen\",\"quantity\":0}"))
           .andExpect(status().isBadRequest());
        then(orderService).shouldHaveNoInteractions();
    }
}

Not every test needs Spring

If a test does not need dependency injection, web handling or persistence, write it as a plain JUnit test. A suite where every test starts Spring takes minutes instead of seconds.

Quick check: Which annotation loads only the web layer for testing a controller with MockMvc?

  • @WebMvcTest
  • @SpringBootTest
  • @DataJpaTest
  • @JsonTest
Answer

@WebMvcTest — @WebMvcTest configures just the MVC infrastructure and the specified controllers.