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

Schema evolution tests

For tests covering upcasters, downcasters, and snapshot upcasters see Testing schema evolution.