Testing
Spring DDD applications have a few distinct testing concerns that call for different strategies.
Event-sourced aggregates are plain Kotlin objects, so both halves of their behaviour — the events a command records and the state those events fold to — are unit-testable with no Spring context, no repository, and no database. The aggregate test fixture (Testing aggregates) drives a command and asserts both, keeping these tests fast and deterministic.
Sagas are plain objects too: their event and deadline handlers can be called directly with test doubles for the command gateway and other collaborators, while the lifecycle — starting, association, completion, and deadline firing — is driven by the framework and verified in an integration test.
Projections (read models) are eventually consistent. An event published inside a write transaction is delivered to the projection asynchronously — the consumer loop wakes on a post-commit signal or falls back to polling, and the JPA-aggregate dispatcher fires only after the writing transaction commits. Tests that assert projection state must therefore poll until the read model reaches the expected value. A container-backed integration test that polls a read model this way needs its rows isolated between tests — see Database cleanup for the framework-owned sweep that keeps those tests independent without touching your own application tables.
In this section
-
Testing aggregates — unit-test aggregates with the
given/whenever/expectfixture. -
Testing projections — delivery timing, Modulith Scenario, Awaitility.
-
Testing sagas — driving and asserting saga flows.
-
Testing schema evolution — upcaster/downcaster fixtures.
-
Database cleanup — sweeping framework tables between integration tests.
Schema evolution tests
For tests covering upcasters, downcasters, and snapshot upcasters see Testing schema evolution.