Conquering NServiceBus part 4 – Testing

by Mikael Henriksson 1. April 2010 15:48

This post is part of a series and the source code can be found at http://github.com/MikaelHenrixon/ConqueringNServiceBus

  1. Conquering NServiceBus part 1 – Getting Started
  2. Conquering NServiceBus part 2 – Initial configuration
  3. Conquering NServiceBus part 3 – A simple Saga
  4. Conquering NServiceBus part 4 – Testing
  5. Conquering NServiceBus part 5 – Troubleshooting DTC
  6. Conquering NServiceBus part 6 – Upgrading StructureMap

This is probably the easiest thing with NServiceBus which hints that testing is extremely important  to the creators. I don’t know the implementation details of the testing but I expect some mocking tool is used for this. It will however totally run your code and if you add a bit of logging inside the Handle(TMessage) you should be be able to follow the flow of events.

To get started we need to add a new assembly to our test project (NServiceBus.Testing). Next thing to do is to initialize the testing. This is preferably done once for every fixture:

 

[TestFixtureSetUp]
public void SetUp()
{
    var assemblies = new[]
                         {
                             typeof (IStandardMessage).Assembly,
                             typeof (BeginSagaMessage).Assembly,
                             typeof (PersistSagaMessage).Assembly,
                             Assembly.Load("NServiceBus"),
                             Assembly.Load("NServiceBus.Core")
                         };

    Test.Initialize(assemblies);
}

 

The easiest thing to get going with is an ordinary message handler and for tutorial purposes I am going to keep the messages really lightweight. The details of my saga is not important and the SagaId you can spot below is mainly for logging purposes.  My PersistSagaMessage sends all the standard messages to another endpoint for processing. The reason why I do this is so that the real (time consuming) work takes place as far away from where the server/client communication takes place. In other words. Every part of the system will do one specific thing only.

public interface IStandardMessage
{
	int Id { get; set; }
	string Msg { get; set; }
}

public class StandardMessage : IStandardMessage
{
	public int Id { get; set; }
	public string Msg { get; set; }
}

public class PersistSageMessage : IMessage
{	
	public int SagaId { get; set; }
	public IList<IStandardMessage> Messages { get; set; }
}

 

And here is an example on how a test for that persistence message handler could look like.

[Test]
public void Verify_that_PersistSagaMessages_are_handled_properly()
{
    Test.Handler<PersistenceMessageHandler>()
        .OnMessage<PersistSagaMessage>(x => { 
            x.SagaId = Guid.NewGuid();
            x.Messages.Add(new StandardMessage
            {
                Id = 1,
                Msg = "First Message"
            });
            x.Contacts.Add(new StandardMessage
            {
                Id = 2,
                Msg = "Second Message"
            });
        });
}

What the test does is the following. It creates a message, wires it to the correct endpoint and runs the correct Handle(TMessage). It’s nice to see the following in the output window:

Persistence: Message received for Saga : 873f2f44-4a79-42ff-b153-5761ca4f97a4 
Persistence: Message processed for Saga : 873f2f44-4a79-42ff-b153-5761ca4f97a4


Actually, when having a look in NH Prof I can see that everything that is not NServiceBus (like my own database calls) runs like a charm. What is happening in the background is that NServiceBus has been mocked out so that there is no need for queues etc and this is as good as integration testing in my opinion. If these tests turn green on me I know the problem is with a name of or permissions to a queue. One thing I also learned while playing around with this is that it’s actually nice to have the message return an integer as soon as possible. This way it’s quite easy to confirm that we hit our target both while testing and running in production.

Now to test saga’s there is a bit more to it and there is of course a lot going on inside the saga handlers. Let’s start with some code. This time I include some expectations and I’ll get to the details of those in just a sec.

[Test]
public void Saga_should_be_complete_after_receivng_complete_command()
{
	Guid sagaId = Guid.NewGuid();
	Test.Saga<AutosyncSaga>(sagaId)
		.ExpectReturn(x => x == 0)
		.ExpectSend<PersistSagaMessage>(x =>
			x.BackupId == sagaId &&
			x.Messages.Count == 1)
		.ExpectPublish<ClearCacheMessage>(x =>
			x.SagaId == sagaId &&
			x.Reason == Reason.Completed)
		.When(x =>
		{
			x.Handle(new BeginSagaMessage
			{
				SagaId = sagaId
			});
			x.Handle(new SomeMessage
			{
				SagaId = sagaId,
				IStandardMessage = new StandardMessage
				{
					Id = 1,
					Msg = "Some stupid message"
				}
			});
			x.Handle(new EndSagaMessage
			{
				SagaId = sagaId,
				Command = Command.Complete,
				MsgCount = 1
			});
		})
		.AssertSagaCompletionIs(true);
}
        

When takes a delegate (action) and there are not many such in the Saga<T> :)  Handle is of course the first thing we want to verify is working. The english version of that code is something like: “When saga handles EndSagaMessage expect that the saga sends PersistSagaMessage and verify that the saga has been completed”. I am not hundred percent sure my code is best practice but what I want to do here is to make sure that the saga is completed after all messages has been handled.

In case the saga did not receive all the messages and there is a count mismatch Bus.Return will be –1 (minus because we are missing messages) what I am using it for now I am just going to delete the whole saga and force the user to restart. I also expect the saga to send a PersistSagaMessage with all the necessary data from the Saga to the endpoint I was talking about earlier.

A quick look in the console output window of Visual Studio gives be the following beautiful result:

Saga: 873f2f44-4a79-42ff-b153-5761ca4f97a4 started
Saga: 873f2f44-4a79-42ff-b153-5761ca4f97a4 received a message with Id : 1
Saga: 873f2f44-4a79-42ff-b153-5761ca4f97a4 data passed on to Persistence 
Saga: 873f2f44-4a79-42ff-b153-5761ca4f97a4 complete

Tags: ,

NServiceBus | Testing

blog comments powered by Disqus

About the author

Life architect specialized in programming