Testing Pyramid in Spring Boot
Fast recall
Quick revision
Core answer
Whether the candidate has a structured view of testing strategy, not just knowledge of individual annotations.
Writing integration tests for everything or ignoring unit tests entirely, creating an inverted pyramid that makes the test suite unbearably slow.
Map each Spring testing annotation to its pyramid layer and explain the trade-off: more tests at the base means faster feedback loops, but you need some higher-layer tests to catch real-world integration bugs.
Interview-ready guide
Interview answer
The testing pyramid is a heuristic for balancing feedback and realism. The broad base contains focused tests that isolate rules and fail clearly. The middle checks real boundaries such as MVC mapping, persistence, or messaging. The top covers a smaller number of critical workflows through a running system. In Spring Boot, that can map to plain JUnit tests, test slices, and broader SpringBootTest or system tests. The exact percentages and timings are not rules; I shape the suite around architecture, risk, feedback speed, and the defects that smaller tests cannot catch.
Testing Pyramid in Spring Boot
Direct Answer
The testing pyramid is a heuristic: keep broad feedback cheap with many focused tests, add fewer boundary and integration tests, and use the most expensive end-to-end checks selectively. It does not prescribe fixed ratios or require every service test to mock a repository. Shape the mix around risk, architecture, execution cost, and the failures each level can detect.
Code Example
// BASE: unit test — fast, isolated business logic
@ExtendWith(MockitoExtension.class)
class OrderServiceTest {
@Mock private OrderRepository repository;
@InjectMocks private OrderService service;
@Test
void shouldApplyMemberDiscount() {
var order = new Order(1L, "item", 100.0);
when(repository.findById(1L)).thenReturn(Optional.of(order));
assertEquals(90.0, service.calculateTotal(1L, true)); // 10% off
}
}
// MIDDLE: slice test — controller layer with MockMvc
@WebMvcTest(OrderController.class)
class OrderControllerTest {
@Autowired private MockMvc mockMvc;
@MockBean private OrderService service; // service still mocked
@Test
void shouldReturnOrderTotal() throws Exception {
when(service.calculateTotal(1L, true)).thenReturn(90.0);
mockMvc.perform(get("/orders/1/total"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.total").value(90.0));
}
}
// TOP: full integration test — end-to-end
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class OrderEndToEndTest {
@Autowired private TestRestTemplate restTemplate;
@Test
void shouldCreateAndRetrieveOrder() { // full flow: API to service to database
var response = restTemplate.postForEntity("/orders",
new Order(null, "item", 100.0), Order.class);
assertEquals(HttpStatus.CREATED, response.getStatusCode());
}
}
Common Mistakes & Pitfalls
- Treating the pyramid as fixed ratios: It is a feedback-and-cost heuristic; system risk and architecture decide the useful mix.
- Assuming broader always means better: A large context can hide the source of failure and still replace important external boundaries.
- Assuming smaller always means sufficient: Unit tests cannot prove mappings, transactions, serialization, or real infrastructure behavior.
- Quoting fixed speedups: Slice and full-context cost depends on configuration, cache reuse, hardware, and external resources.
- Testing implementation details: Prefer observable behavior over incidental internal call order unless that interaction is the contract.
Check your understanding
Answer these without looking back at the article.
- What are test slices in Spring Boot?
- How does @DataJpaTest differ from @SpringBootTest for repository testing?
Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.