Testing non-eventsourced aggregates

Hi there,

I’m trying to use AxonFramework 3.0 for my new project. Initially I decided not to use event sourcing and created a simple GenericMongoRepository to store aggregate state, so I’ve got few question about testing:

  1. AggregateTestFixture uses EventSourcingRepository only. Is it ok to test my aggregates this way while I do not use event sourcing?
  2. I have created unit tests for my GenericMongoRepository, but how do I make sure it properly integrates into Axon logic? What would be the recommended setup for integration testing?

Regards,
Eugene

Hi Eugene,

the AggregateTestFixture is designed to test event sourced Aggregates. That is, Aggregates that have been designed to be event sourced. It’s still possible to use state storage at runtime. If you use state storage, you can revert to “regular” unit tests, where you load an instance in the current state and very behavior of commands.

The best integration test would be to set up a Spring Boot application (or just a setup using the Configuration API when not using Spring Boot) and send a few commands to your aggregate. Assert that the commands are all processed successfully (without throwing exceptions).

Hope this helps.

Allard

Hi Allard,

Thanks for the answer. The thing with ‘normal’ unit testing is that I am still using AggregateLifecycle.apply in command handlers and change the internal state of my aggregates in the corresponding event handlers (is it a valid thing to do if I use state storage after all?). So if I unit test my aggregate I will have to mock the static AggregateLifecycle.apply and test separately my command and event handlers, right?

Kind regards,
Eugene

Hi Eugene,

it sounds like you have built your aggregates in the Event Sourcing way, but just want to store them as state at runtime, am I right?
In that case, you can actually benefit from using the test fixtures.

You’re right about the need to configure a few things in order to be able to use AggregateLifecycle.apply().
You can use the AnnotatedAggregate.initialize method to initialize it. The first parameter is the method that creates your aggregate instance. The second is the model describing the aggregate structure. You can easily create one by using the static method: ModelInspector.inspectAggregate(MyAggregate.class).

If you then interact via the AnnotatedAggregate class with your aggregate instance, the apply() methods will function properly.

Hope this helps.
Cheers,

Allard

Hi Allard,

it sounds like you have built your aggregates in the Event Sourcing way, but just want to store them as state at runtime, am I right?

Right. I read on StackOverflow that other people we using Axon this way - designing aggregates in event sourced style but storing state - and I decided to give it a try. I am still using the normal @EventHandler annotations instead of @EventSourcingHandler and it seems to be working so far.

One additional question: my aggregate get stored even if no events have been emitted which in my case means there was no any change to its internal state. I understand that a normal state repository can not make assumptions on whether the aggregate is clean or dirty after a command handler was called, but is there a way to avoid storing in the case of no events?

Thanks for all the help and great job with Axon!

Kind regards,
Eugene

Hi Eugene,

a little secret (don’t tell anyone :wink: ): in Axon, the @EventHandler and the @EventSourcingHandler are technically identical. In fact there isn’t even any mechanism triggering on either of these annotations. It’s just because they are meta-annotated with @MessageHandler(messageType = EventMessage.class) that makes Axon trigger on them. It is, however, recommended to use @EventSourcingHandler, as it best describes the intent of the method.

Implementing a dirty check based on events is possible, but slightly cumbersome. The problem is that, from a GenericJpaRepository perspective, the aggregate is somewhat a black box. That’s different when using an EventSourcingRepository, where at least one can assume the aggregate is event sourced.
The general approach would be to wrap or subclass the GenericJpaRepository and wrap the EventBus that is passed into it. Around the publish() method, set a dirty flag to true (preferably as a resource in the UnitOfWork). In the save method on your wrapping repository, only forward the save call to the GenericJpaRepository if the flag is set to true.

Cheers,

Allard