diff --git a/Test.cs b/Test.cs index f34c04f..3998f5f 100644 --- a/Test.cs +++ b/Test.cs @@ -6,6 +6,131 @@ namespace EventSourcing.Demo { + public abstract class AggregateTests where TAggregate : AggregateRoot + { + private readonly TAggregate _aggregateRoot; + + protected AggregateTests(TAggregate aggregateRoot) + { + _aggregateRoot = aggregateRoot; + } + + protected void Given(params IEvent[] events) + { + if (events != null) + { + _aggregateRoot.Load(events); + } + } + + protected void When(Action command) + { + command(_aggregateRoot); + } + + protected void Then(params Action[] conditions) + { + var events = _aggregateRoot.GetUncommittedEvents(); + events.Count.ShouldBe(1); + var evnt = events.First(); + evnt.ShouldBeOfType(); + if (conditions != null) + { + ((TEvent)evnt).ShouldSatisfyAllConditions(conditions); + } + } + + protected void Throws(Action command, params Action[] conditions) where TException : Exception + { + var ex = Should.Throw(() => command(_aggregateRoot)); + if (conditions != null) + { + ex.ShouldSatisfyAllConditions(conditions); + } + } + } + + public class GivenWhenThenTests : AggregateTests + { + private readonly Fixture _fixture; + private readonly string _sku = "abc123"; + private readonly int _initialQuantity; + + public GivenWhenThenTests() : base(new WarehouseProduct("abc123")) + { + _fixture = new Fixture(); + _fixture.Customizations.Add(new Int32SequenceGenerator()); + _initialQuantity = (int)_fixture.Create(); + } + + [Fact] + public void ShipProductShouldRaiseProductShipped() + { + Given(new ProductReceived(_sku, _initialQuantity, DateTime.UtcNow)); + + var quantityToShip = _fixture.Create(); + When(x => x.ShipProduct(quantityToShip)); + + Then( + x => x.Quantity.ShouldBe(quantityToShip), + x => x.Sku.ShouldBe(_sku), + x => x.EventType.ShouldBe("ProductShipped")); + } + + [Fact] + public void ShipProductShouldThrowIfNoQuantityOnHand() + { + Given(); + + Throws( + x => x.ShipProduct(1), + x => x.Message.ShouldBe("Cannot Ship to a negative Quantity on Hand.")); + } + + [Fact] + public void ReceiveProductShouldRaiseProductReceived() + { + Given(new ProductReceived(_sku, _initialQuantity, DateTime.UtcNow)); + + var quantityToReceive = _fixture.Create(); + When(x => x.ReceiveProduct(quantityToReceive)); + + Then( + x => x.Quantity.ShouldBe(quantityToReceive), + x => x.Sku.ShouldBe(_sku), + x => x.EventType.ShouldBe("ProductReceived")); + } + + [Fact] + public void AdjustInventoryShouldRaiseProductAdjusted() + { + Given(new ProductReceived(_sku, _initialQuantity, DateTime.UtcNow)); + + var quantityToAdjust = _fixture.Create(); + var reason = _fixture.Create(); + + When(x => x.AdjustInventory(quantityToAdjust, reason)); + + Then( + x => x.Quantity.ShouldBe(quantityToAdjust), + x => x.Sku.ShouldBe(_sku), + x => x.Reason.ShouldBe(reason), + x => x.EventType.ShouldBe("InventoryAdjusted")); + } + + [Fact] + public void AdjustInventoryShouldThrowIfNoQuantityOnHand() + { + Given(); + + var reason = _fixture.Create(); + + Throws( + x => x.AdjustInventory(-1, reason), + x => x.Message.ShouldBe("Cannot adjust to a negative Quantity on Hand.")); + } + } + public class ProductTests { private readonly string _sku; @@ -45,15 +170,6 @@ } [Fact] - public void ShipProductShouldUpdateState() - { - var quantityToShip = _fixture.Create(); - _sut.ShipProduct(quantityToShip); - - _sut.GetQuantityOnHand().ShouldBe(_initialQuantity - quantityToShip); - } - - [Fact] public void ShipProductShouldThrowIfNoQuantityOnHand() { var ex = Should.Throw(() => _sut.ShipProduct(_initialQuantity + 1)); @@ -80,15 +196,6 @@ } [Fact] - public void ReceiveProductShouldUpdateState() - { - var quantityToReceive = _fixture.Create(); - _sut.ReceiveProduct(quantityToReceive); - - _sut.GetQuantityOnHand().ShouldBe(_initialQuantity + quantityToReceive); - } - - [Fact] public void AdjustInventoryShouldRaiseProductAdjusted() { var quantityToAdjust = _fixture.Create(); @@ -110,15 +217,6 @@ } [Fact] - public void AdjustInventoryShouldUpdateState() - { - var quantityToAdjust = _fixture.Create(); - _sut.AdjustInventory(quantityToAdjust, string.Empty); - - _sut.GetQuantityOnHand().ShouldBe(_initialQuantity + quantityToAdjust); - } - - [Fact] public void AdjustInventoryShouldThrowIfNoQuantityOnHand() { var ex = Should.Throw(() => _sut.AdjustInventory((_initialQuantity + 1) * -1, string.Empty)); diff --git a/Test.cs b/Test.cs index f34c04f..3998f5f 100644 --- a/Test.cs +++ b/Test.cs @@ -6,6 +6,131 @@ namespace EventSourcing.Demo { + public abstract class AggregateTests where TAggregate : AggregateRoot + { + private readonly TAggregate _aggregateRoot; + + protected AggregateTests(TAggregate aggregateRoot) + { + _aggregateRoot = aggregateRoot; + } + + protected void Given(params IEvent[] events) + { + if (events != null) + { + _aggregateRoot.Load(events); + } + } + + protected void When(Action command) + { + command(_aggregateRoot); + } + + protected void Then(params Action[] conditions) + { + var events = _aggregateRoot.GetUncommittedEvents(); + events.Count.ShouldBe(1); + var evnt = events.First(); + evnt.ShouldBeOfType(); + if (conditions != null) + { + ((TEvent)evnt).ShouldSatisfyAllConditions(conditions); + } + } + + protected void Throws(Action command, params Action[] conditions) where TException : Exception + { + var ex = Should.Throw(() => command(_aggregateRoot)); + if (conditions != null) + { + ex.ShouldSatisfyAllConditions(conditions); + } + } + } + + public class GivenWhenThenTests : AggregateTests + { + private readonly Fixture _fixture; + private readonly string _sku = "abc123"; + private readonly int _initialQuantity; + + public GivenWhenThenTests() : base(new WarehouseProduct("abc123")) + { + _fixture = new Fixture(); + _fixture.Customizations.Add(new Int32SequenceGenerator()); + _initialQuantity = (int)_fixture.Create(); + } + + [Fact] + public void ShipProductShouldRaiseProductShipped() + { + Given(new ProductReceived(_sku, _initialQuantity, DateTime.UtcNow)); + + var quantityToShip = _fixture.Create(); + When(x => x.ShipProduct(quantityToShip)); + + Then( + x => x.Quantity.ShouldBe(quantityToShip), + x => x.Sku.ShouldBe(_sku), + x => x.EventType.ShouldBe("ProductShipped")); + } + + [Fact] + public void ShipProductShouldThrowIfNoQuantityOnHand() + { + Given(); + + Throws( + x => x.ShipProduct(1), + x => x.Message.ShouldBe("Cannot Ship to a negative Quantity on Hand.")); + } + + [Fact] + public void ReceiveProductShouldRaiseProductReceived() + { + Given(new ProductReceived(_sku, _initialQuantity, DateTime.UtcNow)); + + var quantityToReceive = _fixture.Create(); + When(x => x.ReceiveProduct(quantityToReceive)); + + Then( + x => x.Quantity.ShouldBe(quantityToReceive), + x => x.Sku.ShouldBe(_sku), + x => x.EventType.ShouldBe("ProductReceived")); + } + + [Fact] + public void AdjustInventoryShouldRaiseProductAdjusted() + { + Given(new ProductReceived(_sku, _initialQuantity, DateTime.UtcNow)); + + var quantityToAdjust = _fixture.Create(); + var reason = _fixture.Create(); + + When(x => x.AdjustInventory(quantityToAdjust, reason)); + + Then( + x => x.Quantity.ShouldBe(quantityToAdjust), + x => x.Sku.ShouldBe(_sku), + x => x.Reason.ShouldBe(reason), + x => x.EventType.ShouldBe("InventoryAdjusted")); + } + + [Fact] + public void AdjustInventoryShouldThrowIfNoQuantityOnHand() + { + Given(); + + var reason = _fixture.Create(); + + Throws( + x => x.AdjustInventory(-1, reason), + x => x.Message.ShouldBe("Cannot adjust to a negative Quantity on Hand.")); + } + } + public class ProductTests { private readonly string _sku; @@ -45,15 +170,6 @@ } [Fact] - public void ShipProductShouldUpdateState() - { - var quantityToShip = _fixture.Create(); - _sut.ShipProduct(quantityToShip); - - _sut.GetQuantityOnHand().ShouldBe(_initialQuantity - quantityToShip); - } - - [Fact] public void ShipProductShouldThrowIfNoQuantityOnHand() { var ex = Should.Throw(() => _sut.ShipProduct(_initialQuantity + 1)); @@ -80,15 +196,6 @@ } [Fact] - public void ReceiveProductShouldUpdateState() - { - var quantityToReceive = _fixture.Create(); - _sut.ReceiveProduct(quantityToReceive); - - _sut.GetQuantityOnHand().ShouldBe(_initialQuantity + quantityToReceive); - } - - [Fact] public void AdjustInventoryShouldRaiseProductAdjusted() { var quantityToAdjust = _fixture.Create(); @@ -110,15 +217,6 @@ } [Fact] - public void AdjustInventoryShouldUpdateState() - { - var quantityToAdjust = _fixture.Create(); - _sut.AdjustInventory(quantityToAdjust, string.Empty); - - _sut.GetQuantityOnHand().ShouldBe(_initialQuantity + quantityToAdjust); - } - - [Fact] public void AdjustInventoryShouldThrowIfNoQuantityOnHand() { var ex = Should.Throw(() => _sut.AdjustInventory((_initialQuantity + 1) * -1, string.Empty)); diff --git a/WarehouseProduct.cs b/WarehouseProduct.cs index 29de5a4..69dacd7 100644 --- a/WarehouseProduct.cs +++ b/WarehouseProduct.cs @@ -26,6 +26,8 @@ { _uncommittedEvents.Add(evnt); } + + public abstract void Load(IEnumerable events); } public class WarehouseProduct : AggregateRoot @@ -39,13 +41,18 @@ Sku = sku; } + public override void Load(IEnumerable events) + { + foreach (var evnt in events) + { + Apply(evnt as dynamic); + } + } + public static WarehouseProduct Load(string sku, IEnumerable events) { var warehouseProduct = new WarehouseProduct(sku); - foreach (var evnt in events) - { - warehouseProduct.Apply(evnt as dynamic); - } + warehouseProduct.Load(events); return warehouseProduct; } @@ -107,6 +114,7 @@ { return _warehouseProductState.QuantityOnHand; } + } public class InvalidDomainException : Exception