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
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:
@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@ControllerAdvice@Service@Repository@MockBean
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.
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.
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
@MockBeangiven()when()
@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
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 Type | Annotation | Context loaded | Speed |
|---|---|---|---|
| Unit test | None (plain JUnit + Mockito) | None | Very fast |
| Web slice | @WebMvcTest | Web layer only | Fast |
| JPA slice | @DataJpaTest | JPA layer only | Fast |
| Integration | @SpringBootTest | Full context | Slow |
@SpringBootTest@SpringBootTest