Testing an Event Sourced Aggregate Root

I’ve mentioned tests twice now and not acted on them yet… Since we now have enough infrastructure in place to attempt to exercise a “Slice” down through our Write Model, let’s give it a try.

Let’s look back at our EventStormed “Start Creating Roast Schedule” Command:

The Team could translate this into this Behavior-Driven Development (BDD) Scenario:

Scenario: Setting up a brand new roast schedule
    Given there is no roast schedule created for next week
        When I start creating a roast schedule
        Then an empty roast schedule is created

We’ll get into the invariant “no roast schedule created for next week” later, since it’s important for the Team to work with Nick to really figure out what it means to check if there is already a Roast Schedule when Nick starts scheduling his coffee roasting for the following week.

For right now, let’s focus on how we’re going to exercise the Event Sourced Aggregate Root, trying to stay close to this BDD-style of testing. The techniques I’m going to show you are not original, they come from Greg Young’s DDD/CQRS/Event Sourcing training here and from Mark Nijhof’s Fohjin project here. We’re going to use my preferred xUnit, Moq, and FluentAssertions testing framework “stack” to write tests.

With the Event Sourced Aggregate Root, we want to test that a Command successfully transitions the state of the Aggregate, and emits an Event signifying the success of this state transition. By designing the RoastSchedule Aggregate the way that we have, an Event will not be created if the Command was not successful. Remember, we can say ‘No’ to Commands. Events are business facts that definitely happened.

We’re going to use a base class that allows us to ensure that Given an Aggregate, When a Command is issued to our Aggregate, Then an Event (or set of Events) is published.

Using a BDD naming convention, we’ll call this base class a Specification<TAggregateRoot, TCommand>:

public abstract class Specification<TAggregateRoot, TCommand>
        where TAggregateRoot : AggregateRoot, new()
        where TCommand : Command {

    	...
}

We’ll organize our tests using folders that represent Scenarios:

Our Given will be used to establish the state of the System Under Test… in this case the RoastSchedule Aggregate Root. Given, in an Event Sourced test, is represented by a collection of Events. We can put our System Under Test in any state simply by instantiating a List<Event> that, by replaying, would get the Aggregate into the desired Given state:

public abstract class Specification<TAggregateRoot, TCommand>
        where TAggregateRoot : AggregateRoot, new()
        where TCommand : Command {

    protected virtual IEnumerable<Event> Given() {
        return new List<Event>();
    }

    ...
}

Notice that when we implement a Scenario<TAggregateRoot, TCommand>, we will either override Given() to yield the Events to build up the starting state of the Aggregate or we will just return a new List<Event>() to signify that nothing has happened yet to the Aggregate. The latter implementation will be used for testing the StartCreatingRoastSchedule Command since it will be the first Event in the RoastSchedule Aggregate’s lifetime.

Next would be our When. When will be *When TCommand is sent to the ICommandHandler<TCommand> that we are testing. So the When() method will return a Command:

    public abstract class Specification<TAggregateRoot, TCommand>
            where TAggregateRoot : AggregateRoot, new()
            where TCommand : Command {

        protected virtual IEnumerable<Event> Given() {
            return new List<Event>();
        }

        protected abstract TCommand When();

    	...
}

So to test When I start creating a roast schedule, we’ll have When() return a StartCreatingRoastSchedule Command:

public class When_starting_to_create_a_new_roast_schedule : Specification<RoastSchedule, StartCreatingRoastScheduleCommand> {

    private readonly Guid Id;

    public When_starting_to_create_a_new_roast_schedule() {
        Id = Guid.NewGuid();
    }

    protected override StartCreatingRoastScheduleCommand When() {
        return new StartCreatingRoastScheduleCommand(Id);
    }

    ...
}

We also need a concrete StartCreatingRoastScheduleCommandHandler to handle When:

public abstract class Specification<TAggregateRoot, TCommand>
        where TAggregateRoot : AggregateRoot, new()
        where TCommand : Command {

    protected virtual IEnumerable<Event> Given() {
        return new List<Event>();
    }
    protected abstract TCommand When();
    protected abstract ICommandHandler<TCommand> CommandHandler();

    ...
}

Here’s where we override CommandHandler() to return StartCreatingRoastScheduleCommandHandler:

public class When_starting_to_create_a_new_roast_schedule : Specification<RoastSchedule, StartCreatingRoastScheduleCommand> {

    private readonly Guid Id;

    public When_starting_to_create_a_new_roast_schedule() {
        Id = Guid.NewGuid();
    }

    protected override StartCreatingRoastScheduleCommand When() {
        return new StartCreatingRoastScheduleCommand(Id);
    }

    protected override ICommandHandler<StartCreatingRoastScheduleCommand> CommandHandler() {
        return new StartCreatingRoastScheduleCommandHandler(MockRepository.Object);
    }

    ...
}

Notice MockRepository.Object. Our StartCreatingRoastScheduleCommandHandler(IRepository repository) has a dependency that our test needs to accommodate. We’ll use Moq to mock it. Most importantly, we’re going to set up a .Callback on the setup for the mock of IRepository<TAggregateRoot> that makes sure the original TAggregateRoot property on Specification is replaced by the TAggregateRoot that published the Events:

public abstract class Specification<TAggregateRoot, TCommand>
        where TAggregateRoot : AggregateRoot, new()
        where TCommand : Command {

    protected virtual IEnumerable<Event> Given() {
        return new List<Event>();
    }
    protected abstract TCommand When();
    protected abstract ICommandHandler<TCommand> CommandHandler();
    protected TAggregateRoot AggregateRoot;

    // setup Moq in Specification's constructor...
    protected Mock<IRepository<TAggregateRoot>> MockRepository;

    protected Specification() {

	    ...

	    MockRepository = new Mock<IRepository<TAggregateRoot>>();
	    MockRepository.Setup(x => x.GetById(It.IsAny<Guid>())).Returns(AggregateRoot);
	    // when AggregateRoot is saved, replace Specification.AggregateRoot with
	    // AggregateRoot that has been "acted" on
	    MockRepository.Setup(x => x.Save(It.IsAny<TAggregateRoot>(), It.IsAny<int>()))
	        .Callback<TAggregateRoot, int>((x, _) => AggregateRoot = x);

	    ...
	}
}

Next up - let’s make our Specification runnable…


continue reading…

previous…


I will be releasing most of this content only to subscribers. Make sure you sign up by clicking the big red button!


Related Posts:

Tweet
comments powered by Disqus