Skip to content

Commit

Permalink
Change testing upgrade guide to point to saga scenario testing (#5855)
Browse files Browse the repository at this point in the history
  • Loading branch information
DavidBoike authored Sep 16, 2022
1 parent f9e6736 commit 1a352ef
Show file tree
Hide file tree
Showing 3 changed files with 26 additions and 66 deletions.
16 changes: 7 additions & 9 deletions Snippets/Testing/Testing_7/UpgradeGuides/7to8/FluentSagaTests.cs
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -15,7 +15,6 @@ class FluentSagaTests
public void TestSagaFluent()
{
Test.Saga<MySaga>()
.ExpectReplyToOriginator<MyResponse>()
.ExpectTimeoutToBeSetIn<StartsSaga>(
check: (state, span) =>
{
Expand All @@ -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<MySaga.SagaData>, IAmStartedByMessages<StartsSaga>, IHandleTimeouts<StartsSaga>
{
Expand All @@ -54,7 +53,6 @@ protected override void ConfigureHowToFindSaga(SagaPropertyMapper<SagaData> mapp

public async Task Handle(StartsSaga message, IMessageHandlerContext context)
{
await ReplyToOriginator(context, new MyResponse());
await RequestTimeout(context, TimeSpan.FromDays(7), message);
await context.Publish<MyEvent>();
await context.Send(new MyCommand());
Expand Down
66 changes: 13 additions & 53 deletions Snippets/Testing/Testing_8/UpgradeGuides/7to8/FluentSagaTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<MySaga, MySaga.SagaData>();

// 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<MyEvent>(), 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<MyCommand>(), 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
Expand Down Expand Up @@ -103,7 +64,6 @@ protected override void ConfigureHowToFindSaga(SagaPropertyMapper<SagaData> mapp

public async Task Handle(StartsSaga message, IMessageHandlerContext context)
{
await ReplyToOriginator(context, new MyResponse());
await RequestTimeout(context, TimeSpan.FromDays(7), message);
await context.Publish<MyEvent>();
await context.Send(new MyCommand());
Expand Down
10 changes: 6 additions & 4 deletions nservicebus/upgrades/testing-7to8.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down

0 comments on commit 1a352ef

Please sign in to comment.