Docs

Koto.Testing

DDD-specific test helpers: Given/When/Then for aggregates.

What's included

TypePurpose
AggregateTestFixture<TAgg>Fluent Given/When/Then for aggregate unit tests
AggregateAssertions<TAgg>Domain event assertions
FakeRepository<TAgg, TId>In-memory IRepository<TAgg,TId>
FakeIntegrationEventPublisherCaptures published IIntegrationEvents
ResultAssertions<T>.Should().BeSuccess(), .BeFailure(), .BeFailureWith("code"), .HaveErrors("code1", "code2"), .HaveValue(x)

Aggregate unit tests

public class OrderTests
{
    [Fact]
    public void Cancel_raises_OrderCancelled_event()
    {
        new AggregateTestFixture<Order>()
            .Given(new OrderPlaced(orderId, customerId))
            .When(o => o.Cancel("Customer request"))
            .Then()
            .ShouldHaveRaisedEvent<OrderCancelled>(e => e.Reason == "Customer request")
            .And.ShouldHaveRaisedExactly(1);
    }
}

Result assertions

var result = order.Cancel("reason");

result.Should().BeSuccess();
result.Should().BeFailureWith("orders.order.already-cancelled");
result.Should().HaveErrors("general.value.is-required", "general.invalid-length"); // all codes, in order
result.Should().HaveValue(expectedValue);

Fake infrastructure

var repo = new FakeRepository<Order, OrderId>();
var publisher = new FakeIntegrationEventPublisher();

// ... run use case ...

repo.All.Should().ContainSingle();
publisher.GetPublishedEvent<OrderPlacedEvent>().OrderId.Should().Be(orderId);

IAggregateApply<TEvent> (optional)

Implement on your aggregate to enable state reconstruction in Given():

public class Order : AggregateRoot<OrderId>, IAggregateApply<OrderPlaced>
{
    public void Apply(OrderPlaced e) => Status = OrderStatus.Pending;
}