Testing NServiceBus with fluent style

Component: Testing
NuGet Package NServiceBus.Testing (7-pre)
Target NServiceBus Version: 7.x
This page targets a pre-release version and is subject to change prior to the final release.
Generally it is recommended to use Arrange-Act-Assert style tests. To learn how to test NServiceBus using Arrange-Act-Assert (AAA) approach, refer to the dedicated article.

Structure

Tests can be performed using the fluent API or in the traditional Arrange-Act-Assert (AAA) fashion.

The tests in this article present fluent API usage. To learn how to test NServiceBus using Arrange-Act-Assert (AAA) approach, refer to the dedicated article.

Handlers

Testing handlers focuses on their externally visible behavior - the types of messages they send or reply with.

[TestFixture]
public class Tests
{
    [Test]
    public void TestHandler()
    {
        Test.Handler<MyHandler>()
            .ExpectReply<ResponseMessage>(
                check: message =>
                {
                    return message.String == "hello";
                })
            .OnMessage<RequestMessage>(
                initializeMessage: message =>
                {
                    message.String = "hello";
                });
    }
}

public class MyHandler :
    IHandleMessages<RequestMessage>
{
    public Task Handle(RequestMessage message, IMessageHandlerContext context)
    {
        var reply = new ResponseMessage
        {
            String = message.String
        };
        return context.Reply(reply);
    }
}

The test verifies that when a message of the type RequestMessage is processed by MyHandler, it responds with a message of the type ResponseMessage. Also, the test checks that if the request message's String property value is "hello" then that should be the value of the String property of the response message.

Sagas

Testing sagas focuses on their externally visible behavior - the types of messages they send or reply with, but it's also possible to verify that saga requested a timeout or was completed.

[Test]
public void TestSaga()
{
    Test.Saga<MySaga>()
        .ExpectReplyToOriginator<MyResponse>()
        .ExpectTimeoutToBeSetIn<StartsSaga>(
            check: (state, span) =>
            {
                return span == TimeSpan.FromDays(7);
            })
        .ExpectPublish<MyEvent>()
        .ExpectSend<MyCommand>()
        .When(
            sagaIsInvoked: (saga, context) =>
            {
                return saga.Handle(new StartsSaga(), context);
            })
        .ExpectPublish<MyOtherEvent>()
        .WhenSagaTimesOut()
        .AssertSagaCompletionIs(true);
}

The test verifies that when a message of the type StartsSaga is processed by MySaga, the saga replies to the sender with the MyResponse message, publishes MyEvent, sends MyCommand and requests a timeout for message StartsSaga. Also it checks if the saga publishes MyOtherEvent and is completed, after the timeout expires.

Note that the expectation for MyOtherEvent is set only after the message is sent.

Interface messages

To support testing of interface messages use .WhenHandling<T>() method, where T is the interface type.

Header manipulation

Message handlers retrieve information from the incoming message headers and set headers for the outgoing messages, for example NServiceBus uses that set correlation Id or address for reply. Headers can be also used for passing custom information.

[TestFixture]
public class Tests
{
    [Test]
    public void Run()
    {
        Test.Handler<MyMessageHandler>()
            .SetIncomingHeader("MyHeaderKey", "myHeaderValue")
            .ExpectReply<ResponseMessage>(
                check: (message, replyOptions) =>
                {
                    var headers = replyOptions.GetHeaders();
                    return headers["MyHeaderKey"] == "myHeaderValue";
                })
            .OnMessage<RequestMessage>(
                initializeMessage: message =>
                {
                    message.String = "hello";
                });
    }
}

class MyMessageHandler :
    IHandleMessages<RequestMessage>
{
    public Task Handle(RequestMessage message, IMessageHandlerContext context)
    {
        var header = context.MessageHeaders["MyHeaderKey"];

        var responseMessage = new ResponseMessage();

        var options = new ReplyOptions();
        options.SetHeader("MyHeaderKey", header);
        return context.Reply(responseMessage, options);
    }
}

This test asserts that the value of the outgoing header has been set.

Injecting additional dependencies into the service layer

Many of the message handling classes in the service layer make use of other objects to perform their work. When testing these classes, replace those objects with "stubs" so that the class under test is isolated.

[TestFixture]
public class Tests
{
    [Test]
    public void RunWithDependencyInjection()
    {
        var mockService = new MyService();
        var injectionHandler = new WithDependencyInjectionHandler(mockService);
        var handler = Test.Handler(injectionHandler);
        // Rest of test
    }
}

class WithDependencyInjectionHandler :
    IHandleMessages<MyMessage>
{
    MyService myService;

    public WithDependencyInjectionHandler(MyService myService)
    {
        this.myService = myService;
    }

    public Task Handle(MyMessage message, IMessageHandlerContext context)
    {
        // use myService here
        return Task.CompletedTask;
    }
}

Samples


Last modified