Unit testing - rebus-org/Rebus GitHub Wiki

When you are developing software, you probably want to ensure that you can bend and flex your code and refactor it to your heart's desire without breaking anything.

You probably do that by writing automated tests that test units of your system at various levels.

This page will describe three different levels of testing with Rebus.

Full in-memory bus

Since Rebus can be configured with an in-memory transport and in-memory persistence, you can get away with testing many things by starting a full Rebus instance to participate in your tests without risking tests interfering with each other, or risking that the environment somehow affects the outcome of your tests.

This approach can be very useful to verify that messages flow like you expect them to, since you can easily spin up multiple endpoints that communicate using the in-mem transport - you just need to pass the same Network instance to them so that they can reach each other - e.g. like so:

[Test]
public void DoSomeTesting() 
{
	var network = new InMemNetwork();

	var containerAdapter1 = GetContainerAdapter1();
	var containerAdapter2 = GetContainerAdapter2();

	using (var bus1 = CreateBus("b1", network, containerAdapter1))
	using (var bus2 = CreateBus("b2", network, containerAdapter2))
	{
		// do stuff in here
	}
}

IBus CreateBus(string inputQueueName, InMemNetwork network, IContaineAdapter containerAdapter)
{
	return Configure.With(containerAdapter)
		.Transport(t => t.UseInMemoryTransport(network, inputQueueName))
		.Start();
}

Unit testing a single handler

Sometimes you might want to be sure that one particular handler makes certain calls when a message is dispatched to it. Since message handlers just need to implement IHandleMessages<TMessage> your handler is just an ordinary class with a Handle method on it, like so:

public class MyMessageHandler : IHandleMessage<MyMessage>
{
	public async Task Handle(MyMessage message)
	{
		// whee!
	}
}

You can - obviously - punish MyMessageHandler as hard as you wish in your tests, and you will not be bothered by Rebus in any way. But what if we want to verify that MyMessageHandler sends an AnotherMessage when it receives a MyMessage with a certain name in it?

Consider this slightly modified version of MyMessageHandler:

public class MyMessageHandler : IHandleMessage<MyMessage>
{
	readonly IBus _bus;

	public MyMessageHandler(IBus bus)
	{
		_bus = bus;
	}

	public async Task Handle(MyMessage message)
	{
		if (message.Name == "El Duderino")
		{
			await _bus.Send(new AnotherMessage());
		}
	}
}

Since mocking IBus can be pretty tedious (mainly because all methods are async and return Tasks), you can now benefit from using Rebus' FakeBus to participate in this kind of test.

In this case, the test would look like this:

[Test]
public void CheckThatWeReactProperlyToTheDude()
{
	var bus = new FakeBus();
	var handler = new MyMessageHandler(bus);

	handler.Handle(new MyMessage { Name="El Duderino" }).Wait();

	var messageSentEvents = bus.Events.OfType<MessageSent<AnotherMessage>>();
	Assert.That(messageSentEvents.Count(), Is.EqualTo(1));
}

As you can see, FakeBus records everything that happens to it as events. And these events can be queried with your ordinary LINQ via the Events property, which returns an IEnumerable<FakeBusEvent>.

FakeBusEvent derivations exist for MessageSent, MessageDeferred, MessagePublished, etc.

Unit testing a saga

Unit testing a saga handler is a little more involved. Saga handler are derived from Rebus' Saga<TSagaData> class, so you inherit a little bit of behavior with it.

Moreover, a saga needs to set up correlation properties, i.e. how to figure out which saga instance to load for each incoming message.

Even though it would be possible to just new up your saga handler and dispatch a message to it by calling a Handle method on it, this kind of testing would skip a crucial part of the logic associated with a saga: Message correlation!

Therefore - to make it easier to test sagas, including whichever correlation you have set up - Rebus comes with a SagaFixture<TSagaHandler>.

Say, for instance, that you want to test a saga called MySaga - that could be done like this:

using(var fixture = SagaFixture.For<MySaga>())
{
	// perform test in here
}

or - if the saga handler does not come with a default constructor:

using(var fixture = SagaFixture.For(() => new MySaga(someDependency)))
{
	// perform test in here
}

The saga fixture has all kinds of neat things on it. You can deliver messages to it by calling its Deliver method, like so:

fixture.Deliver("are you there?");

and you can check existing saga data instances by doing some LINQ on the saga fixture's Data property, e.g. like so:

var myInstance = fixture.Data
    .OfType<MySagaData>()
    .First(d => d.CorrelationId == "blah");

Internally, saga fixture uses a full in-memory Rebus bus - it just blocks until the message has been fully processed when you Deliver a message to it, and then its in-mem saga storage exposes all contained saga data instances to be queries using the syntax shown above.

Happy testing! 😁

⚠️ **GitHub.com Fallback** ⚠️