Unit Testing

Developing enterprise-scale distributed systems is hard and testing them is just as challenging a task. The architectural approach supported by NServiceBus makes these challenges more manageable. And the testing facilities provided actually make unit testing endpoints and workflows easy. You can now develop your service layers and long-running processes using test-driven development.

Getting started

NServiceBus ships with a stand alone testing helper nuget package that makes testing a lot simpler.
To install this package:

Install-Package NServiceBus.Testing

Once the package is installed you need ensure that you call Test.Initialize() (or any of its overloads) before executing any test method.

Unit testing the service layer

The service layer in an NServiceBus application is made from message handlers. Each class typically handles one specific type of message. Testing these classes usually focuses on their externally visible behavior: the types of messages they send or reply with. This is as simple to test as could be expected:

public class TestHandler
{
    [Test]
    public void Run()
    {
        Test.Initialize();

        Test.Handler<MyHandler>()
            .ExpectReply<ResponseMessage>(m => m.String == "hello")
            .OnMessage<RequestMessage>(m => m.String = "hello");
    }
}

public class MyHandler :
    IHandleMessages<RequestMessage>
{
    public IBus Bus { get; set; }

    public void Handle(RequestMessage message)
    {
        var reply = new ResponseMessage
        {
            String = message.String
        };
        Bus.Reply(reply);
    }
}

This test says that when a message of the type RequestMessage is processed by MyHandler, it responds with a message of the type ResponseMessage. Also, if the request message's String property value is "hello" than that is also the value of the String property of the response message.

Testing a Saga

public class MyTest
{
    [Test]
    public void Run()
    {
        Test.Initialize();
        Test.Saga<MySaga>()
                .ExpectReplyToOrginator<MyResponse>()
                .ExpectTimeoutToBeSetIn<StartsSaga>((state, span) => span == TimeSpan.FromDays(7))
                .ExpectPublish<MyEvent>()
                .ExpectSend<MyCommand>()
            .When(s => s.Handle(new StartsSaga()))
                .ExpectPublish<MyEvent>()
            .WhenSagaTimesOut()
                .AssertSagaCompletionIs(true);
    }
}

public class MySaga : NServiceBus.Saga.Saga<MySagaData>,
    IAmStartedByMessages<StartsSaga>,
    IHandleTimeouts<StartsSaga>
{
    public void Handle(StartsSaga message)
    {
        ReplyToOriginator(new MyResponse());
        Bus.Publish(new MyEvent());
        Bus.Send(new MyCommand());
        RequestTimeout(TimeSpan.FromDays(7), message); 
    }

    public void Timeout(StartsSaga state)
    {
        Bus.Publish<MyEvent>();
        MarkAsComplete();
    }
}

Testing interface messages

To support testing of interface messages v5 introduces a .WhenHandling<T>() method where T is the interface type.

Testing header manipulation

It is the responsibility of the message handlers in the service layer is to use data from headers found in the request to make decisions, and to set headers on top of the response messages. This is how this kind of functionality can be tested:

public class MyTest
{
    [Test]
    public void Run()
    {
        Test.Initialize();

        Test.Handler<MyMessageHandler>()
            .SetIncomingHeader("Test", "abc")
            .ExpectReply<ResponseMessage>(m => Test.Bus.GetMessageHeader(m, "MyHeaderKey") == "myHeaderValue")
            .OnMessage<RequestMessage>(m => m.String = "hello");
    }
}

class MyMessageHandler : IHandleMessages<RequestMessage>
{
    public IBus Bus { get; set; }

    public void Handle(RequestMessage message)
    {
        var responseMessage = new ResponseMessage();
        Bus.SetMessageHeader(responseMessage, "MyHeaderKey", "myHeaderValue");
        Bus.Reply(responseMessage);
    }
}

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. Here's how:

public class MyTest
{
    [Test]
    public void RunWithDependencyInjection()
    {
        Test.Initialize();

        var mockService = new MyService();
        Test.Handler(bus => new WithDependencyInjectionHandler(mockService));
        //Rest of test
    }
}

class WithDependencyInjectionHandler : IHandleMessages<MyMessage>
{
    MyService myService;

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

    public void Handle(MyMessage message)
    {
    }
}


Last modified 2014-11-18 01:59:36Z