/Verify.NServiceBus

Adds Verify support to verify NServiceBus Test Contexts

Primary LanguageC#MIT LicenseMIT

Verify.NServiceBus

Discussions Build status NuGet Status

Adds Verify support to verify NServiceBus Test Contexts.

See Milestones for release notes.

NuGet package

https://nuget.org/packages/Verify.NServiceBus/

Usage

[ModuleInitializer]
public static void Initialize() =>
    VerifyNServiceBus.Initialize();

snippet source | anchor

Verifying a context

Given the following handler:

public class MyHandler :
    IHandleMessages<MyRequest>
{
    public async Task Handle(MyRequest message, HandlerContext context)
    {
        await context.Publish(
            new MyPublishMessage
            {
                Property = "Value"
            });

        await context.Reply(
            new MyReplyMessage
            {
                Property = "Value"
            });

        var sendOptions = new SendOptions();
        sendOptions.DelayDeliveryWith(TimeSpan.FromHours(12));
        await context.Send(
            new MySendMessage
            {
                Property = "Value"
            },
            sendOptions);

        await context.ForwardCurrentMessageTo("newDestination");
    }
}

snippet source | anchor

The test that verifies the resulting context:

[Fact]
public async Task VerifyHandlerResult()
{
    var handler = new MyHandler();
    var context = new TestableMessageHandlerContext();

    var message = new MyRequest();
    await handler.Handle(message, context);

    await Verify(context);
}

snippet source | anchor

The resulting verification file is as follows:

{
  RepliedMessages: [
    {
      MyReplyMessage: {
        Property: Value
      }
    }
  ],
  ForwardedMessages: [
    newDestination
  ],
  SentMessages: [
    {
      MySendMessage: {
        Property: Value
      },
      Options: {
        DeliveryDelay: 12:00:00
      }
    }
  ],
  PublishedMessages: [
    {
      MyPublishMessage: {
        Property: Value
      }
    }
  ]
}

snippet source | anchor

Recording

Recording allows all message interaction with the test context to be captured and then verified.

Switch usages of TestableMessageHandlerContext to RecordingHandlerContext.

[Fact]
public async Task VerifyHandlerResult()
{
    var handler = new MyHandler();
    var context = new RecordingHandlerContext();

    var message = new MyRequest();
    await handler.Handle(message, context);

    await Verify("some other data");
}

snippet source | anchor

The resulting context verification file is as follows:

{
  target: some other data,
  messages: [
    {
      Publish: {
        MyPublishMessage: {
          Property: Value
        }
      }
    },
    {
      Reply: {
        MyReplyMessage: {
          Property: Value
        }
      }
    },
    {
      Send: {
        MySendMessage: {
          Property: Value
        },
        Options: {
          DeliveryDelay: 12:00:00
        }
      }
    }
  ]
}

snippet source | anchor

Verifying a Saga

Given the following handler:

public class MySaga :
    NServiceBus.Saga<MySaga.MySagaData>,
    IHandleMessages<MyRequest>
{
    protected override void ConfigureHowToFindSaga(SagaPropertyMapper<MySagaData> mapper) =>
        mapper.ConfigureMapping<MyRequest>(message => message.OrderId)
            .ToSaga(sagaData => sagaData.OrderId);

    public async Task Handle(MyRequest message, HandlerContext context)
    {
        await context.Publish(
            new MyPublishMessage
            {
                Property = "Value"
            });

        Data.MessageCount++;
    }

    public class MySagaData :
        ContainSagaData
    {
        public Guid OrderId { get; set; }
        public int MessageCount { get; set; }
    }
}

snippet source | anchor

The test that verifies the resulting context:

[Fact]
public async Task VerifySagaResult()
{
    var saga = new MySaga
    {
        Data = new()
    };

    var context = new TestableMessageHandlerContext();

    var message = new MyRequest();
    await saga.Handle(message, context);

    await Verify(new
    {
        context,
        saga
    });
}

snippet source | anchor

The resulting verification file is as follows:

{
  context: {
    PublishedMessages: [
      {
        MyPublishMessage: {
          Property: Value
        }
      }
    ]
  },
  saga: {
    MessageCount: 1
  }
}

snippet source | anchor

Example behavior change

The next time there is a code change, that results in a different resulting interactions with NServiceBus, those changes can be visualized. For example if the DelayDeliveryWith is changed from 12 hours to 1 day:

await context.Publish(
    new MyPublishMessage
    {
        Property = "Value"
    });

await context.Reply(
    new MyReplyMessage
    {
        Property = "Value"
    });

var sendOptions = new SendOptions();
sendOptions.DelayDeliveryWith(TimeSpan.FromDays(1));
await context.Send(
    new MySendMessage
    {
        Property = "Value"
    },
    sendOptions);

await context.ForwardCurrentMessageTo("newDestination");

snippet source | anchor

Then the resulting visualization diff would look as follows:

visualization diff

Message to Handler mapping

MessageToHandlerMap allows verification of message that do not have a handler.

For example:

var map = new MessageToHandlerMap();
map.AddMessagesFromAssembly<MyMessage>();
map.AddHandlersFromAssembly<MyHandler>();
await Verify(map);

snippet source | anchor

Would result in:

{
  MessagesWithNoHandler: [
    MessageToHandlerMapTests.MessageWithNoHandler
  ]
}

snippet source | anchor

Icon

Approval designed by Mike Zuidgeest from The Noun Project.