Event Sourcing & CQRS · Event Sourcing in Practice

Testing Event-Sourced Systems

Given-when-then tests for aggregates and projections.

Event sourcing makes behavior tests expressive: Given past events, When a command is handled, Then expect new events (or an error). Projection tests: given events, expect read-model rows. These tests are fast (no database for aggregates), document business rules, and serve as regression tests for upcasters (old events → expected current shape).

Test shapes

Test type Given When Then
Aggregate behavior Past events Command New events / error
Projection Events Projector runs Read-model rows
Upcaster Old event JSON Upcast Current schema
Process manager Events Policy reacts Commands issued
def test_cannot_withdraw_more_than_balance():
    given = [{"type": "AccountOpened", "account_id": "a1"}, {"type": "MoneyDeposited", "amount": 100}]
    with pytest.raises(DomainError, match="Insufficient funds"):
        decide_withdraw(rehydrate(given), amount=150)

def test_withdrawal_emits_event():
    given = [{"type": "AccountOpened", "account_id": "a1"}, {"type": "MoneyDeposited", "amount": 100}]
    assert decide_withdraw(rehydrate(given), amount=40) == [{"type": "MoneyWithdrawn", "amount": 40}]

Common pitfalls

  • Testing internal state instead of emitted events.
  • No tests for old event versions.
  • Projection tests that depend on a shared database state.

Key points

  • Given-when-then on events
  • Test upcasters with real old payloads
  • Fast, database-free aggregate tests

Further reading: Martin Fowler: Given When Then

Practice questions

Why are given-when-then tests natural for event-sourced aggregates?

Answer

Aggregate state is defined by past events (given), behavior by commands (when) and outcomes by emitted events (then), so tests read like business rules.

How do you test upcasters?

Answer

Store real examples of old event payloads as fixtures and assert they upcast to the expected current schema.

Open in the app to mark lessons complete, take notes and track your progress.