diff --git a/Snippets/Testing/Testing_7/UpgradeGuides/7to8/FluentSagaTests.cs b/Snippets/Testing/Testing_7/UpgradeGuides/7to8/FluentSagaTests.cs index f6a2a14adba..177259f804d 100644 --- a/Snippets/Testing/Testing_7/UpgradeGuides/7to8/FluentSagaTests.cs +++ b/Snippets/Testing/Testing_7/UpgradeGuides/7to8/FluentSagaTests.cs @@ -1,10 +1,10 @@ namespace Testing_7.UpgradeGuides._7to8 { - using System; - using System.Threading.Tasks; using NServiceBus; using NServiceBus.Testing; using NUnit.Framework; + using System; + using System.Threading.Tasks; [Explicit] #region 7to8-testsaga @@ -15,7 +15,6 @@ class FluentSagaTests public void TestSagaFluent() { Test.Saga() - .ExpectReplyToOriginator() .ExpectTimeoutToBeSetIn( check: (state, span) => { @@ -35,11 +34,11 @@ public void TestSagaFluent() } #endregion - class StartsSaga : IMessage {} - class MyResponse : IMessage {} - class MyEvent : IEvent {} - class MyCommand : ICommand {} - class MyOtherEvent : IEvent {} + class StartsSaga : IMessage { } + class MyResponse : IMessage { } + class MyEvent : IEvent { } + class MyCommand : ICommand { } + class MyOtherEvent : IEvent { } class MySaga : NServiceBus.Saga, IAmStartedByMessages, IHandleTimeouts { @@ -54,7 +53,6 @@ protected override void ConfigureHowToFindSaga(SagaPropertyMapper mapp public async Task Handle(StartsSaga message, IMessageHandlerContext context) { - await ReplyToOriginator(context, new MyResponse()); await RequestTimeout(context, TimeSpan.FromDays(7), message); await context.Publish(); await context.Send(new MyCommand()); diff --git a/Snippets/Testing/Testing_8/UpgradeGuides/7to8/FluentSagaTests.cs b/Snippets/Testing/Testing_8/UpgradeGuides/7to8/FluentSagaTests.cs index 58711449569..6f2f88d4eb6 100644 --- a/Snippets/Testing/Testing_8/UpgradeGuides/7to8/FluentSagaTests.cs +++ b/Snippets/Testing/Testing_8/UpgradeGuides/7to8/FluentSagaTests.cs @@ -16,65 +16,26 @@ class ArrangeActAssertSagaTests public async Task When_Saga_is_started_by_StartsSaga() { // Arrange - var saga = new MySaga - { - Data = new MySaga.SagaData - { - Originator = "Originator" - } - }; + var testableSaga = new TestableSaga(); // Act - var message = new StartsSaga(); - var messageHandlerContext = new TestableMessageHandlerContext(); - - await saga.Handle(message, messageHandlerContext); + var message = new StartsSaga { MyId = "some-id" }; + var startResult = await testableSaga.Handle(message); // Assert - Assert.IsTrue(messageHandlerContext.RepliedMessages.Any(x => - x.Message is MyResponse && - x.Options.GetDestination() == "Originator"), - "A MyResponse reply should be sent to the originator" - ); - - Assert.IsTrue(messageHandlerContext.TimeoutMessages.Any(x => - x.Message is StartsSaga && - x.Within == TimeSpan.FromDays(7)), - "The StartsSaga message should be deferred for 7 days" - ); - - Assert.IsTrue(messageHandlerContext.PublishedMessages.Any(x => - x.Message is MyEvent), - "MyEvent should be published" - ); + Assert.That(startResult.FindPublishedMessage(), Is.Not.Null, + "MyEvent should be published"); - Assert.IsTrue(messageHandlerContext.SentMessages.Any(x => - x.Message is MyCommand), - "MyCommand should be sent" - ); - } + Assert.That(startResult.FindSentMessage(), Is.Not.Null, + "MyCommand should be sent"); - [Test] - public async Task When_StartsSaga_Timeout_completes() - { - // Arrange - var saga = new MySaga - { - Data = new MySaga.SagaData() - }; - - // Act - var timeoutMessage = new StartsSaga(); - var timeoutHandlerContext = new TestableMessageHandlerContext(); - await saga.Timeout(timeoutMessage, timeoutHandlerContext); - - // Assert - Assert.IsTrue(timeoutHandlerContext.PublishedMessages.Any(x => - x.Message is MyOtherEvent), - "MyOtherEvent should be published" - ); + // Instead of asserting on timeouts placed, virtually advance time + // and then assert on the results + var advanceTimeResults = await testableSaga.AdvanceTime(TimeSpan.FromDays(7)); - Assert.IsTrue(saga.Completed, "Saga should be completed"); + Assert.That(advanceTimeResults.Length, Is.EqualTo(1)); + var timeoutResult = advanceTimeResults.Single(); + Assert.That(timeoutResult.Completed, Is.True); } } #endregion @@ -103,7 +64,6 @@ protected override void ConfigureHowToFindSaga(SagaPropertyMapper mapp public async Task Handle(StartsSaga message, IMessageHandlerContext context) { - await ReplyToOriginator(context, new MyResponse()); await RequestTimeout(context, TimeSpan.FromDays(7), message); await context.Publish(); await context.Send(new MyCommand()); diff --git a/nservicebus/upgrades/testing-7to8.md b/nservicebus/upgrades/testing-7to8.md index 0bde0231f03..0d2632cb71f 100644 --- a/nservicebus/upgrades/testing-7to8.md +++ b/nservicebus/upgrades/testing-7to8.md @@ -28,15 +28,17 @@ See [the handler unit testing documentation](/nservicebus/testing/#testing-a-han ### Testing a saga -To test a saga, create it with the necessary dependencies (including the `Data` property), then call the `Handle` method directly. Pass in an instance of `TestableMessageHandlerContext` which will collect all outgoing operations. This context allows customization of metadata about the incoming message, including headers. +To test a saga, use the [saga scenario testing framework](/nservicebus/testing/saga-scenario-testing.md). The `TestableSaga` class emulates a saga that can receive multiple messages, stores saga data in between messages, and can virtually advance time to observe the processing of timeouts. Assertions can be made on the result of any message being processed by the testable saga. -Fluent-style saga tests will often include multiple state changes. Arrange-Act-Assert (AAA) tests should test a single state change in isolation. The state of the saga can be configured manually before each test as part of the Arrange step. +For each operation, a custom `TestableMessageHandlerContext` can be supplied, but if not provided one will be generated by the testing framework to limit unnecessary boilerplate. -This is an example showing two state changes. The first starts the saga that triggers a timeout, then sends a reply, publishes an event, and sends a message. The second state change happens when the timeout occurs, causing another event to be published, and the saga to be completed. These can be split into two tests (one for each state change) when using the Arrange-Act-Assert style. +This is an example showing two state changes. The first starts the saga that triggers a timeout, publishes an event, and sends a message. The second state change happens when the timeout occurs, causing another event to be published, and the saga to be completed. snippet: 7to8-testsaga -See [the saga unit testing documentation](/nservicebus/testing/#testing-a-saga) for more information. +Fluent-style saga tests will often include multiple state changes. Arrange-Act-Assert (AAA) tests normally test a single state change in isolation, however this is difficult with sagas because too many internal details of how NServiceBus sagas work must be encoded into the test, limiting their effectiveness. For this reason the scenario test pattern is recommended. + +It is also possible to [test sagas in isolation](/nservicebus/testing/#testing-a-saga) in a manner more similar to handlers. ### Uniform Session