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.

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 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")
            .OnMessage<RequestMessage>(m => m.String = "hello");

        Assert.AreEqual("myHeaderValue", Test.Bus.OutgoingHeaders["MyHeaderKey"]);
    }
}

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

    public void Handle(RequestMessage message)
    {
        Bus.OutgoingHeaders["MyHeaderKey"] = "myHeaderValue";
        Bus.Reply(new 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-08-21 03:30:11Z