Testing — Cheat Sheet
Spring Boot · 5 topics. Download the PDF or the Instagram carousel and share it.
@SpringBootTest
@SpringBootTest loads the full application context for integration tests; use webEnvironment=RANDOM_PORT for tests that exercise the real HTTP stack.
- ✓@SpringBootTest(webEnvironment=RANDOM_PORT) starts a real server — use TestRestTemplate for HTTP calls; best for end-to-end smoke tests.
- ✓@WebMvcTest loads only the MVC layer — fast controller tests; mock all service and repository beans with @MockBean.
- ✓@DataJpaTest loads only JPA + in-memory H2 and rolls back each test — ideal for testing @Query correctness.
- ✓@MockBean replaces the real Spring bean with a Mockito mock; @SpyBean wraps the real bean (real methods unless stubbed).
- ✓Prefer test slices over full @SpringBootTest for speed — only use @SpringBootTest for true end-to-end integration tests.
- ✓Use @TestPropertySource or @SpringBootTest(properties=…) to override application.yml values for specific tests.
// Full integration test — real HTTP stack, random port
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class OrderControllerIntegrationTest {
@Autowired
TestRestTemplate restTemplate; // auto-configured for RANDOM_PORT
@Test
void placeOrder_shouldReturn201AndOrderId() {
OrderRequest request = new OrderRequest("PROD-1", 2);
ResponseEntity<OrderResponse> response = restTemplate.postForEntity(
"/api/orders", request, OrderResponse.class);
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.CREATED);
assertThat(response.getBody().getOrderId()).isNotNull();
}
}
// No server — test service layer directly
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE)
class OrderServiceIntegrationTest {
@Autowired OrderService orderService;
@Autowired OrderRepository orderRepository;
@Test
@Transactional
void placeOrder_shouldPersistOrder() {
Order order = orderService.placeOrder(new OrderRequest("PROD-1", 2));
assertThat(orderRepository.findById(order.getId())).isPresent();
}
}MockMvc Testing
MockMvc dispatches requests through the MVC pipeline without starting a real server, enabling fast controller tests with request/response assertions.
- ✓Standalone setup tests the controller in isolation (no Spring context) — fastest; use for unit tests.
- ✓@AutoConfigureMockMvc with @SpringBootTest tests the full pipeline including security filters and real beans.
- ✓@MockBean replaces a real Spring bean in the context with a Mockito mock — needed when using @SpringBootTest.
- ✓@WithMockUser (spring-security-test) injects a mock authentication into the SecurityContext for security tests.
- ✓andDo(print()) logs the full request/response to stdout — invaluable during test development.
- ✓jsonPath uses Jayway JsonPath syntax: $.content[0].id, $.totalElements, $.content.length().
@ExtendWith(MockitoExtension.class)
class OrderControllerTest {
@Mock
private OrderService orderService;
private MockMvc mockMvc;
@BeforeEach
void setUp() {
mockMvc = MockMvcBuilders
.standaloneSetup(new OrderController(orderService))
.setControllerAdvice(new GlobalExceptionHandler()) // include if needed
.build();
}
@Test
void getOrder_returnsOrder() throws Exception {
Order order = new Order(1L, "PENDING", new BigDecimal("99.99"));
when(orderService.findById(1L)).thenReturn(order);
mockMvc.perform(get("/api/orders/1")
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("$.id").value(1))
.andExpect(jsonPath("$.status").value("PENDING"))
.andExpect(jsonPath("$.total").value(99.99));
verify(orderService).findById(1L);
}
@Test
void createOrder_validatesInput() throws Exception {
String invalidJson = """{"customerId": null, "total": -10}""";
mockMvc.perform(post("/api/orders")
.contentType(MediaType.APPLICATION_JSON)
.content(invalidJson))
.andExpect(status().isBadRequest())
.andExpect(jsonPath("$.errors").isArray());
}
}@WebMvcTest
Slices the context to only MVC infrastructure and the specified controller, making tests lighter; beans not in scope must be mocked with @MockBean.
- ✓@WebMvcTest loads only MVC infrastructure — faster than @SpringBootTest but tests the real MVC pipeline.
- ✓All service/repository beans must be provided via @MockBean; they are not auto-scanned in the slice.
- ✓Spring Security IS included; use @WithMockUser, @WithAnonymousUser, or mock UserDetailsService as needed.
- ✓@ControllerAdvice beans are automatically included in the slice — test exception handler mappings here.
- ✓Specify the controller class in @WebMvcTest(MyController.class) to load only that controller and reduce context size.
- ✓Use @Import to add custom beans or configuration to the slice without loading the full application context.
@WebMvcTest(OrderController.class) // only loads OrderController + MVC infrastructure
class OrderControllerTest {
@Autowired
private MockMvc mockMvc;
@Autowired
private ObjectMapper objectMapper;
@MockBean // provides mock to OrderController
private OrderService orderService;
@MockBean
private OrderMapper orderMapper;
@Test
void getOrder_found_returns200() throws Exception {
OrderDto dto = new OrderDto(1L, "PENDING", new BigDecimal("99.99"));
when(orderService.findById(1L)).thenReturn(dto);
mockMvc.perform(get("/api/orders/1"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.id").value(1))
.andExpect(jsonPath("$.status").value("PENDING"));
}
@Test
void getOrder_notFound_returns404() throws Exception {
when(orderService.findById(99L))
.thenThrow(new OrderNotFoundException(99L));
mockMvc.perform(get("/api/orders/99"))
.andExpect(status().isNotFound())
.andExpect(jsonPath("$.message").value("Order 99 not found"));
}
@Test
void createOrder_invalidBody_returns400() throws Exception {
// empty customerId triggers @NotBlank validation
String json = """{"customerId":"","total":50.0}""";
mockMvc.perform(post("/api/orders")
.contentType(MediaType.APPLICATION_JSON)
.content(json))
.andExpect(status().isBadRequest());
}
}@DataJpaTest
Loads only the JPA layer, auto-configures an in-memory DB, and rolls back each test; ideal for testing repository query correctness in isolation.
- ✓@DataJpaTest loads JPA layer only — no controllers, no services, no @Component beans.
- ✓Each test runs in a transaction that rolls back automatically — no teardown required.
- ✓H2 in-memory database is used by default; add @AutoConfigureTestDatabase(replace=NONE) to use a real DB.
- ✓TestEntityManager wraps EntityManager with helpers like persist(), flush(), find() useful for test setup.
- ✓Use Testcontainers for MySQL-specific features (JSON functions, window functions, Flyway migrations).
- ✓Import @EnableJpaAuditing configuration explicitly if it is not auto-applied in the slice.
@DataJpaTest // loads only JPA slice; H2 replaces configured DB
class OrderRepositoryTest {
@Autowired
private TestEntityManager em;
@Autowired
private OrderRepository orderRepository;
@Test
void findByStatus_returnsPendingOrders() {
// Arrange: persist test data via TestEntityManager
Customer customer = em.persist(new Customer("john@example.com"));
em.persist(new Order(customer, "PENDING", new BigDecimal("50.00")));
em.persist(new Order(customer, "SHIPPED", new BigDecimal("30.00")));
em.persist(new Order(customer, "PENDING", new BigDecimal("20.00")));
em.flush();
// Act
List<Order> result = orderRepository.findByStatus("PENDING");
// Assert
assertThat(result).hasSize(2)
.extracting(Order::getStatus)
.containsOnly("PENDING");
}
@Test
void findTopSpenders_returnsCustomersSortedByRevenue() {
// Test a native query with aggregation
// ...
List<CustomerRevenue> top = orderRepository.findTopSpenders(
PageRequest.of(0, 5));
assertThat(top).isSortedAccordingTo(
Comparator.comparing(CustomerRevenue::getTotalRevenue).reversed());
}
}Testcontainers with Spring Boot
Testcontainers spin up real Docker containers (Postgres, Kafka, Redis) for integration tests; the @Testcontainers + @Container annotations manage lifecycle.
- ✓Testcontainers starts real Docker containers for integration tests — no fake in-memory DBs
- ✓@ServiceConnection (Boot 3.1+) auto-configures datasource URL / broker address from the container
- ✓Declare containers as static fields so they start once per test class, not per test method
- ✓@ImportTestcontainers shares a container configuration class across multiple test classes
- ✓TESTCONTAINERS_REUSE_ENABLE=true skips restart between runs — speeds up local TDD
- ✓Use @DynamicPropertySource for pre-3.1 Spring Boot to feed container URLs to the context
@SpringBootTest
@Testcontainers
class OrderServiceIntegrationTest {
@Container
@ServiceConnection // auto-configures spring.datasource.url from the container
static PostgreSQLContainer<?> postgres =
new PostgreSQLContainer<>("postgres:16");
@Container
@ServiceConnection // auto-configures spring.kafka.bootstrap-servers
static KafkaContainer kafka =
new KafkaContainer(DockerImageName.parse("confluentinc/cp-kafka:7.6.0"));
@Autowired OrderService orderService;
@Autowired OrderRepository orderRepo;
@Test
void shouldPersistOrder() {
Order saved = orderService.place(new CreateOrderRequest("SKU-1", 2));
assertThat(orderRepo.findById(saved.getId())).isPresent();
}
}