An Executable Specification

In the last post, we started to write tests in a way that would result in a readable specification. Borrowing from Greg Young and Mark Nijhof, we created a base class called Specification that allows us to test our Event Sourced Aggregate Roots in a Given-When-Then format.

We left off with our Specification<TAggregateRoot, TCommand> class’s Given() and When() methods defined:

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);

	    ...
	}
}

And an actual test case partially written that sets up a fixture using Specification:

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);
    }

    ...
}

But this doesn’t execute. We need to now assert that Then an empty roast schedule is created. We can tie this all the way back to our orange stickie in the EventStorming done in the beginning of this whole project: “Roast Schedule Created.” This means we need to assert that a RoastScheduleCreatedEvent was published by RoastSchedue, signifying that StartCreatingRoastScheduleCommand was successful.

Since any class that implements AggregateRoot tracks un-committed (not persisted to the Event Store yet) Events in its List<Event> _changes property, we simply need to test that a RoastScheduleCreatedEvent is the last Event in the RoastSchedule._changes collection.

We add a PublishedEvents property to Specification<TAggregateRoot, TCommand> base class to collect all of the Events that are published during the execution of a Specification:

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;
    protected Mock<IRepository<TAggregateRoot>> MockRepository;

    // collects published events for assertion
    protected IEnumerable<Event> PublishedEvents;
    protected Exception CaughtException;

    protected Specification() {

    	...

    }
}

Also, let’s make sure we can actually say Then an Event happened. We’ll simply inherit our testing framework’s test attribute (xUnit’s [Fact] in this case):

public class ThenAttribute : FactAttribute { }

Finally, we exercise our Given-When-Then by instantiating TAggregateRoot, loading it from its Event history (if we are setting it up in a certain state), and sendingTCommand to the ICommandHandler<TCommand>:

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

    ...

    protected Specification() {

    	...

        AggregateRoot = new TAggregateRoot();
        AggregateRoot.LoadFromHistory(Given());

        try {
            CommandHandler().Handle(When());
            PublishedEvents = new List<Event>(AggregateRoot.GetUncommittedChanges());
        }
        catch (Exception exception) {
            CaughtException = exception;
        }
    }
}

Our Specification:

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

Gets fully written like this:

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);
    }

    [Then]
    public void Then_a_roast_schedule_created_event_will_be_published() {
        PublishedEvents.Last().As<RoastScheduleCreatedEvent>().Id.Should().Be(Id);
    }
}

And it passes:

The results of the passing test don’t read exactly like our BDD specification, but more work could be done to make them do that. This would make them truly “executable specifications” that could be handed to First Pop Coffee to confirm that its business requirements are being met.


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