Testing Axon 5 Aggregates with Spock — Patterns and Gotchas

Following up on our migration experience post, we’ve written up how we test Axon 5 aggregates with Spock and the new AxonTestFixture.

Most Axon testing examples online are JUnit-based, but Spock’s given-when-then blocks map beautifully to event-sourced aggregates — you express prior events as “given,” the command as “when,” and expected events or exceptions as “then.”

A few things we learned that aren’t obvious from the docs:

1. AxonTestFixture replaces AggregateTestFixture. Setup now goes through EventSourcingConfigurer:

fixture = AxonTestFixture.with(
    EventSourcingConfigurer.create()
        .registerEntity(EventSourcedEntityModule.autodetected(String, OrderAggregate)),
    customization -> customization.disableAxonServer()
        .registerIgnoredField(OrderPlacedEvent, "placedAt")
)
  1. Timestamp handling is the first thing that bites you. If your command handlers call LocalDateTime.now(), exact event matching breaks. registerIgnoredField() on the timestamp fields is the cleanest workaround we found.

  2. Event chaining builds state naturally. To test a command that requires a confirmed order, you replay the creation and confirmation events:

  fixture.given()
      .event(new OrderPlacedEvent("order-1", "cust-1", items, now))
      .event(new OrderConfirmedEvent("order-1", now))
      .when()
      .command(new ShipOrderCommand("order-1", "TRACK-123"))
      .then()
      .success()

This isn’t a test trick — it’s exactly how event sourcing works in production. The fixture replays the events through @EventSourcingHandler methods, then runs the command against the reconstituted state.

  1. Guard clause rejections are the most valuable tests. For an aggregate with N states and M commands, you want every invalid state+command combination covered. Each test is ~10 lines and runs in milliseconds.

  2. Injected services work via registerInjectableResource(). If your command handler takes a PricingService parameter, register a Spock Stub in setup. The fixture injects it automatically.

Full write-up with a complete order lifecycle example, collection state testing, and test suite structure: Testing Axon 5 Aggregates with Spock: A Practical Guide

Happy to answer questions if anyone else is testing Axon 5 with Spock (or Groovy in general).

1 Like

Thanks for cross posting your findings, @petrmac! Pretty sure there are a lot of people that can get value out of this.