⌂ Index
Kapitel 31 · Testing

Spring Test, MockMvc, WebTestClient und Testcontainers

Typ: LibraryVersion 2 ausführlich

Spring Test und Testcontainers ermöglichen Unit-, Slice-, Integration- und End-to-End-nahe Tests mit realen Infrastrukturabhängigkeiten.

Fachliche Einordnung

Enterprise-Tests müssen schnell und glaubwürdig sein. Mocks allein finden keine SQL-, Mapping-, Security- oder Messaging-Probleme; komplette End-to-End-Tests allein sind zu langsam.

Enterprise-Merksatz: Spring Test und Testcontainers ermöglichen Unit-, Slice-, Integration- und End-to-End-nahe Tests mit realen Infrastrukturabhängigkeiten.

Technische Darstellung

Test Spring Context MockMvc/WebTestClient Testcontainer Database/Broker Assertions
Kernkonzepte
  • @SpringBootTest für vollständigen Kontext.
  • @WebMvcTest, @DataJpaTest und andere Slices.
  • MockMvc für MVC, WebTestClient für reactive und auch Server-Tests.
  • Testcontainers für echte Datenbanken, Kafka, RabbitMQ usw.
  • Testpyramide plus Contract Tests.
Wann einsetzen?
  • Du willst DB- und Messaging-Verhalten realistisch prüfen.
  • Du möchtest API-Verhalten ohne echten Server testen.
  • Du brauchst reproduzierbare lokale Infrastrukturtests.
Typische Fehler und Risiken
  • Jeder Test startet kompletten Kontext.
  • Testcontainers Images nicht versionieren.
  • Zu viele Mocks verdecken Integrationsfehler.
Legacy- und Modernisierungssicht

Vor Modernisierung werden Legacy-Charakterisierungstests ergänzt; neue Spring-Adapter erhalten Container-Integrationstests gegen echte Infrastruktur.

Ausführliches Beispiel

Das Beispiel zeigt bewusst nicht nur Annotationen, sondern auch die Verantwortung der Schicht. In echten Projekten sollte der technische Spring-Code an Adapter- oder Konfigurationsrändern bleiben, während die Fachlogik testbar und möglichst frameworkarm bleibt.

@SpringBootTest
@Testcontainers
class OrderRepositoryIntegrationTest {
    @Container
    static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:17-alpine");

    @DynamicPropertySource
    static void datasource(DynamicPropertyRegistry registry) {
        registry.add("spring.datasource.url", postgres::getJdbcUrl);
        registry.add("spring.datasource.username", postgres::getUsername);
        registry.add("spring.datasource.password", postgres::getPassword);
    }

    @Test
    void storesOrder() {
        OrderEntity saved = repository.save(OrderEntity.draft("C-100"));
        assertThat(repository.findById(saved.id())).isPresent();
    }
}
Checkliste für Reviews
  • Ist die Verantwortung des Bausteins klar: Framework steuert Lebenszyklus, Library wird gezielt benutzt?
  • Ist die Fachlogik außerhalb von Controller, Listener, Repository-Implementierung oder Konfiguration?
  • Sind Fehlerfälle, Timeouts, Security, Monitoring und Tests sichtbar modelliert?
  • Gibt es klare Grenzen zwischen DTO, Domäne, Persistence und Infrastruktur?