How Event Sourcing can improve your tests

Mattias Holmqvist
Dec 7, 2017 · 4 min read

Building your domain model with event sourcing is maybe not simpler than using a stateful snapshot-style modeling where everything is available at hand.

There are however a few compelling reasons that makes it a very nice tool to use when the thing you’re building is really business-critical.

In this post I will shed some light on some nice characteristics you get from event sourcing when it comes to testing your business logic.


Modeling your domain in a functional style

The methods in an event sourced aggregate root can be designed in different ways but the end result should always be that a command results in zero to n events, depending on the current state of the aggregate.

Lately, we have experimented with a functional style where all commands in the aggregate root actually are functions that return an optional list of events. This creates a generic api structure which is both quite simple to understand and definitely easy to test!

We have separated the current state of our aggregate root to a separate object, which is basically a holder for the Value Objects that the aggregate root can be initialised with. To test our business logic, we perform the following steps.

  1. Load current state from event.

How you load your current state may differ depending on language features and code style. We like to use a Builder pattern when implementing this in Java (see this example) but regardless of what you use this step is not particularly difficult.

2. Initialise your aggregate root.

Once the state has been loaded we need to initialise the aggregate root with the current state. We only use Value Objects here to ensure the state is not modified during the command operation.

Commands should not modify the state of the aggregate root directly! The command should make the aggregate root emit an event which is saved to the event store. During the next loading of the aggregate root the state will now be affected by the last event that was saved.

3. Execute your command!

Once the aggregate root is initialised, the command can now be executed. This is where your most important business rules live. This code should be clean and understandable and even possible to explain to your business people without much translation.

4. Verify your events

The command will result in the emission of events from the aggregate root. The unit test can now simply verify the business rules by asserting that the correct events are emitted and (perhaps) what data they contain.

You don’t need any fancy frameworks or tools

This is one of the most important things that we’ve stressed when talking to clients when working as consultants and what we still stress today when we talk to people about using Serialized IO.

Keep your domain code infrastructure-free!

An example

Below is an example of a unit test from our samples-java repository. It is in Java and JUnit but should be quite simple to port to any other language/test framework.

One minor gotcha in this example is that the current state might be invalid since we’re just adding events by instantiating them in the test. When the application is running all events are created via the aggregate root so to be really thorough we should create an empty aggregate root and drive all our events from that empty initial state.

@Test
public void payCorrectAmount() {
// given
// Initialise the current state from the events
OrderState state = OrderState.builder(newOrderId())
.apply(orderPlaced(newCustomer(), new Amount(200)))
.build();
// Initialise the aggregate root with the current state
Order order = new Order(state.orderStatus, state.orderAmount);
// when
// execute the command pay()
List<OrderEvent> events = order.pay(new Amount(200));

// then
assertThat(
noEventsOfType(events, PaymentReceivedEvent.class), is(1)
);
assertThat(
noEventsOfType(events, OrderFullyPaidEvent.class), is(1)
);
}

The assertions in this example are simply checking the types of the emitted events but of course it is also possible to inspect the event data more thoroughly if you’d like.

Summary

A nice feature of event sourcing your aggregates is that you get a very strict, yet powerful structure of the code for your core business. You can also implement this style in any programming language. This makes the code easy to follow and easy to test. We have frequently used this style of testing the domain logic and really like it.

Maybe give this style of domain model implementation a try by trying out Serialized IO. It works for any event sourced domain model implemented in any programming language.

Give it a try and tell us what you think!

SERIALIZED

FULLY MANAGED EVENT SOURCING PLATFORM

Mattias Holmqvist

Written by

CEO/Co Founder @ Serialized https://serialized.io

SERIALIZED

FULLY MANAGED EVENT SOURCING PLATFORM

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade