Example of Self-documenting Unit Test with Event Sourcing
One of the biggest advantages of event sourcing approach is it's inherent capability to turn unit tests into a living documentation. Below is an example of specification that I've worked my way through today (that's how NUnit prints it out).
registrations: duplicate email fails - Passed
Environment:
index includes email("contact@lokad.com")
When: Register 'd6e64e':
Customer Name: Lokad
Contact Email: contact@lokad.com
Date: 2011-12-27
Expectations:
[Passed] Registration 'd6e64e':
Customer Name: Lokad
Contact Email: contact@lokad.com
Date: 2011-12-27
[Passed] Registration 'd6e64e' failed:
Email 'contact@lokad.com' is already taken.
And here's how actual NUnit code looks like:
public Specification duplicate_email_fails()
{
var info = new RegistrationInfoBuilder("contact@lokad.com", "Lokad").Build();
var index = new MockUniquenessService();
return new RegistrationSpec(index)
{
Before = {() => index.includes_email("contact@lokad.com")},
When = new CreateRegistration(reg, info),
Expect =
{
new RegistrationCreated(reg, info),
new RegistrationFailed(reg, new[]
{
"Email 'contact@lokad.com' is already taken."
})
},
Finally = index.Clear
};
}
All was achieved without any special magic or even fancy tools. I've just pulled over sources of SimpleTesting and CompareObjects for additional readability.
For those who are interested in RegistrationSpec
class, it is just a simple snippet wiring together dependencies of aggregate root to a strongly-typed specification deriving from TypedSpecification
in SimpleTesting
:
public sealed class RegistrationSpec : AggregateSpecification<RegistrationId>
{
public RegistrationSpec(IRegistrationUniquenessService service)
{
Factory = (events, observer) =>
{
var state = new RegistrationAggregateState(events);
return new RegistrationAggregate(state, observer, service,
new TestPassword(),
new TestIdentity());
};
}
}
Explicit strong-typing of aggregates (as described in bliki) works all the way back in unit test specification by allowing to benefit from compiler-time checking and IntelliSense support. In other words: you don't need to navigate through hundreds of messages to figure out which ones are actually applicable in test.
Published: December 27, 2011.
🤗 Check out my newsletter! It is about building products with ChatGPT and LLMs: latest news, technical insights and my journey. Check out it out