…is pretty easy, because message handlers are ordinary classes free of dependencies – i.e. you implement the appropriate Handle methods, but you don’t have to derive your class off of some base class, and you can have your own dependencies injected, so you’re free to mock everything if you feel like it.
There’s one thing, though: The IBus interface, which you’ll most likely need some time, is kind of clunky to mock with mocking libraries like Moq, Rhino Mocks, NSubstitute, FakeItEasy [insert today’s hip mocking library here] – especially if you’re testing the AAA way of writing your unit tests.
Let’s take a look at a simple handler, whose responsibility is to subscribe to the PurchaseRecorded event and, for each debtor involved in the purchased mortgage deed, ensure that a process is kicked off that subscribes that debtor to an SSN-based address update service provided by the Danish Central Person Registry:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
public class EnsureDebtorsAreSubscribedToAddressUpdates : IHandleMessages<PurchaseRecorded> { readonly IBus bus; public EnsureDebtorsAreSubscribedToAddressUpdates(IBus bus) { this.bus = bus; } public void Handle(PurchaseRecorded message) { foreach(DebtorInfo debtor in message.PurchaseInfo.Debtors) { bus.SendLocal(new EnsureDebtorIsSubscribed(debtor.Ssn)); } } } |
Right, so now I want my test to capture the fact that the incoming event gives rise to multiple messages that the service sends to itself, one for each debtor. My test might look somewhat like this (shown with the classic Rhino Mocks syntax):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
[Test] public void WhenPurchaseIsRecordedAllDebtorsGetSubscribed() { var bus = MockRepository.GenerateMock<IBus>(); var handler = new EnsureDebtorsAreSubscribedToAddressUpdates(bus); var evt = new PurchaseRecorded { Debtors = { new Debtor{Ssn = "123"}, new Debtor{Ssn = "999"}, } }; handler.Handle(evt); // aww, this is clunky! bus.AssertWasCalled(b => b.SendLocal(Arg<EnsureDebtorIsSubscribed>.Matches(e => e.Ssn == "123")); bus.AssertWasCalled(b => b.SendLocal(Arg<EnsureDebtorIsSubscribed>.Matches(e => e.Ssn == "999")); } |
It doesn’t require that much imagination to see how the asserts can become completely unreadable if the sent messages contain more than a few fields… which is why Rebus has a FakeBus in the Rebus.Testing namespace!
Check this out:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
[Test] public void WhenPurchaseIsRecordedAllDebtorsGetSubscribed() { var bus = new FakeBus(); var handler = new EnsureDebtorsAreSubscribedToAddressUpdates(bus); var evt = new PurchaseRecorded { Debtors = { new Debtor{Ssn = "123"}, new Debtor{Ssn = "999"}, } }; handler.Handle(evt); var list = bus.LocallySentMessages.OfType<EnsureDebtorIsSubscribed>().ToList(); Assert.That(list.Count, Is.EqualTo(2)); Assert.That(list[0].Ssn, Is.EqualTo("123")); Assert.That(list[1].Ssn, Is.EqualTo("999")); } |
The example above is quite simple, so it might not be that apparent – but the real force of FakeBus is that just stores all messages that are sent, sent to self, published, replied, etc., also it also stores any headers that may have been added. This way, during testing, you can easily get access to the actually sent messages and inspect whether their information is as expected.