Testing in Spring Boot: Ein Leitfaden für Unit-, Slice- und Integrationstests
Die Test-Pyramide in Spring Boot
Spring Boot kennt drei Test-Ebenen. Jede hat ihren Zweck, ihre Geschwindigkeit und ihre Kosten. Die Kunst ist, die richtige Ebene für jeden Test zu wählen.
/\ Integrationstests (@SpringBootTest)
/ \ Langsam, aber realistisch
/----\ Slice-Tests (@WebMvcTest, @DataJpaTest)
/ \ Schnell, fokussiert
/________\ Unit-Tests (JUnit 5, Mockito)
Blitzschnell, isoliert
Unit-Tests: Die Basis
Unit-Tests prüfen eine einzelne Klasse in Isolation. Kein Spring-Kontext, keine Datenbank, kein HTTP. Nur reine Logik.
class OrderServiceTest {
private OrderRepository repo;
private OrderService service;
@BeforeEach
void setUp() {
repo = mock(OrderRepository.class);
service = new OrderService(repo);
}
@Test
void shouldCalculateTotal() {
// Arrange
var order = new Order(List.of(
new OrderItem("Book", 25.0, 2),
new OrderItem("Pen", 3.0, 5)
));
when(repo.save(any())).thenReturn(order);
// Act
var total = service.calculateTotal(order);
// Assert
assertEquals(65.0, total);
verify(repo).save(order);
}
}
Das AAA-Pattern (Arrange-Act-Assert) strukturiert jeden Test:
- Arrange — Testdaten vorbereiten
- Act — die zu testende Methode aufrufen
- Assert — das Ergebnis prüfen
Slice-Tests: Fokussiert und schnell
Spring Boot bietet Slice-Tests, die nur eine Schicht laden — nicht den ganzen
Kontext. Sie sind 10-100x schneller als @SpringBootTest.
@WebMvcTest — nur die Web-Schicht
@WebMvcTest(OrderController.class)
class OrderControllerTest {
@Autowired
MockMvc mockMvc;
@MockBean
OrderService orderService;
@Test
void shouldReturnOrderById() throws Exception {
when(orderService.findById(1L)).thenReturn(new OrderDto(1L, "Test"));
mockMvc.perform(get("/api/orders/1"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.name").value("Test"));
}
}
Lädt nur OrderController + JSON-Serialisierung + Validierung. Keine Datenbank,
keine Services. @MockBean ersetzt OrderService durch einen Mockito-Mock.
@DataJpaTest — nur die JPA-Schicht
@DataJpaTest
class OrderRepositoryTest {
@Autowired
OrderRepository repository;
@Test
void shouldFindByName() {
repository.save(new Order("Test Order"));
var found = repository.findByName("Test Order");
assertThat(found).isNotEmpty();
}
}
Lädt nur JPA + eine H2-In-Memory-Datenbank. Keine Web-Schicht, keine Services.
Slice-Tests sind 10-100x schneller als @SpringBootTest
@SpringBootTest lädt den gesamten Kontext: Datenbank, Web, Security, Cache. Für "läuft die App überhaupt?" wichtig, aber zu langsam für 200+ Tests. Slice-Tests (@WebMvcTest, @DataJpaTest) laden nur eine Schicht — und dauern Millisekunden statt Sekunden.
Integrationstests: @SpringBootTest
Wenn Sie den gesamten Stack testen wollen — von HTTP bis Datenbank — verwenden
Sie @SpringBootTest:
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
class OrderFlowTest {
@Autowired
TestRestTemplate restTemplate;
@Test
void shouldCreateAndRetrieveOrder() {
// Create
var response = restTemplate.postForEntity("/api/orders",
new OrderDto(null, "Test Order"), OrderDto.class);
assertEquals(HttpStatus.CREATED, response.getStatusCode());
// Retrieve
var id = response.getBody().id();
var found = restTemplate.getForEntity("/api/orders/" + id, OrderDto.class);
assertEquals("Test Order", found.getBody().name());
}
}
Wann verwenden? Rauchtest beim CI/CD, kritische User-Flows, Smoke-Tests. Nicht für jede Business-Logik — dafür sind Unit-Tests schneller.
Test-Organisation: Given-When-Then
Neben AAA (Arrange-Act-Assert) ist auch BDD (Given-When-Then) verbreitet:
@Test
void shouldRejectInvalidEmail() {
// Given
var user = new User("", "invalid-email");
// When
var result = validator.validate(user);
// Then
assertThat(result.hasErrors()).isTrue();
assertThat(result.getFieldError("email")).isNotNull();
}
Spring Boot Course: Vertiefung
Dieser Artikel deckt die Grundlagen von Modul 7 unseres Spring Boot-Kurses ab. Im Kurs lernen Sie jedes Konzept interaktiv:
→ Testing — Modul 7 → Spring Core & DI — Modul 2 → Validation — Modul 4
✅ Wichtige Erkenntnisse
Haken setzen, um deinen Lernfortschritt zu markieren: