Spring Boot: Testing with @SpringBootTest & MockMvc

Spring Boot provides a rich set of testing utilities. @SpringBootTest loads the full application context for integration tests, while slice tests like @WebMvcTest and @DataJpaTest load only the relevant portion of the context, making them faster and more focused.

Contents

@SpringBootTest

@SpringBootTest loads the complete Spring application context. Use it for end-to-end integration tests.

Java
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.beans.factory.annotation.Autowired;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;

@SpringBootTest
class OrderServiceIntegrationTest {

    @Autowired
    private OrderService orderService;

    @Test
    void shouldPlaceOrderSuccessfully() {
        OrderRequest request = new OrderRequest("product-1", 2, "user@example.com");
        Order order = orderService.placeOrder(request);
        assertThat(order.getId()).isNotNull();
        assertThat(order.getStatus()).isEqualTo(OrderStatus.PLACED);
    }
}

Start a real HTTP server for testing REST endpoints:

Java
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class OrderControllerIT {

    @Autowired
    private TestRestTemplate restTemplate;

    @Test
    void shouldReturnOrderById() {
        ResponseEntity<Order> response = restTemplate.getForEntity("/orders/1", Order.class);
        assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
    }
}

@WebMvcTest (Slice Test)

@WebMvcTest loads only the web layer (controllers, filters, @ControllerAdvice). It does not start a real HTTP server and does not load @Service or @Repository beans. Use @MockBean for dependencies.

Java
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.mock.mockito.MockBean;

@WebMvcTest(OrderController.class)  // only loads the OrderController slice
class OrderControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @MockBean
    private OrderService orderService;  // service is mocked

    // test methods...
}

MockMvc Basics (GET / POST)

MockMvc sends requests to your controllers without starting a server.

Java
import org.springframework.test.web.servlet.MockMvc;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;

@Test
void shouldGetOrder() throws Exception {
    Order order = new Order(1L, "product-1", OrderStatus.PLACED);
    given(orderService.findById(1L)).willReturn(order);

    mockMvc.perform(get("/orders/1")
            .accept(MediaType.APPLICATION_JSON))
        .andExpect(status().isOk())
        .andExpect(jsonPath("$.id").value(1))
        .andExpect(jsonPath("$.status").value("PLACED"));
}

@Test
void shouldCreateOrder() throws Exception {
    String requestBody = """
        {"productId": "product-1", "quantity": 2, "email": "user@example.com"}
        """;
    Order created = new Order(10L, "product-1", OrderStatus.PLACED);
    given(orderService.placeOrder(any())).willReturn(created);

    mockMvc.perform(post("/orders")
            .contentType(MediaType.APPLICATION_JSON)
            .content(requestBody))
        .andExpect(status().isCreated())
        .andExpect(jsonPath("$.id").value(10));
}

Asserting Response Status and Body

Check the status and the JSON fields of the response.

Java
mockMvc.perform(get("/orders/999"))
    .andExpect(status().isNotFound())
    .andExpect(jsonPath("$.error").value("Order not found"));

// Assert response body as string
MvcResult result = mockMvc.perform(get("/orders"))
    .andExpect(status().isOk())
    .andReturn();

String body = result.getResponse().getContentAsString();
assertThat(body).contains("\"status\":\"PLACED\"");

// Multiple assertions chained
mockMvc.perform(get("/products")
        .param("category", "electronics"))
    .andExpect(status().isOk())
    .andExpect(content().contentType(MediaType.APPLICATION_JSON))
    .andExpect(jsonPath("$").isArray())
    .andExpect(jsonPath("$.length()").value(3))
    .andExpect(jsonPath("$[0].name").isNotEmpty());

@MockBean

@MockBean creates a Mockito mock and registers it in the Spring application context, replacing any existing bean of that type. Mockito's given() / when() stubs define its behaviour.

Java
@WebMvcTest(UserController.class)
class UserControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @MockBean
    private UserService userService;

    @Test
    void shouldReturnUser() throws Exception {
        User user = new User(1L, "alice", "alice@example.com");
        given(userService.findById(1L)).willReturn(user);

        mockMvc.perform(get("/users/1"))
            .andExpect(status().isOk())
            .andExpect(jsonPath("$.username").value("alice"));

        // Verify the service was called once
        verify(userService, times(1)).findById(1L);
    }

    @Test
    void shouldReturn404WhenUserNotFound() throws Exception {
        given(userService.findById(99L)).willThrow(new UserNotFoundException("Not found"));

        mockMvc.perform(get("/users/99"))
            .andExpect(status().isNotFound());
    }
}

Testing with @DataJpaTest

@DataJpaTest loads only JPA-related components (entities, repositories) and configures an in-memory H2 database by default. It wraps each test in a transaction that is rolled back after the test.

Java
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager;

@DataJpaTest
class OrderRepositoryTest {

    @Autowired
    private TestEntityManager em;

    @Autowired
    private OrderRepository orderRepository;

    @Test
    void shouldFindOrdersByStatus() {
        // Arrange — persist test data
        em.persist(new Order(null, "product-1", OrderStatus.PLACED));
        em.persist(new Order(null, "product-2", OrderStatus.SHIPPED));
        em.flush();

        // Act
        List<Order> placedOrders = orderRepository.findByStatus(OrderStatus.PLACED);

        // Assert
        assertThat(placedOrders).hasSize(1);
        assertThat(placedOrders.get(0).getProductId()).isEqualTo("product-1");
    }
}

Integration Test vs Unit Test

Test TypeAnnotationContext loadedSpeed
Unit testNone (plain JUnit + Mockito)NoneVery fast
Web slice@WebMvcTestWeb layer onlyFast
JPA slice@DataJpaTestJPA layer onlyFast
Integration@SpringBootTestFull contextSlow
Prefer slice tests over @SpringBootTest for the majority of tests. Reserve @SpringBootTest for a small number of end-to-end integration tests that validate the full wiring of the application.