Ayende @ Rahien

My name is Oren Eini
Founder of Hibernating Rhinos LTD and RavenDB.
You can reach me by phone or email:


+972 52-548-6969

, @ Q c

Posts: 6,131 | Comments: 45,563

filter by tags archive

Testing Rhino Service Bus applications

time to read 8 min | 1551 words

One of the really nice things about Rhino Service Bus applications is that we have created a structured way to handle inputs and outputs. You have messages coming in and out, as well as the endpoint local state to deal with. You don’t have to worry about how to deal with external integration points, because those are already going over messages.

And when you have basic input/output figured out, you are pretty much done.

For example, let us see the code that handles extending trail licenses in our ordering system:

public class ExtendTrialLicenseConsumer : ConsumerOf<ExtendTrialLicense>
    public IDocumentSession Session { get; set; }
    public IServiceBus Bus { get; set; }

    public void Consume(ExtendTrialLicense message)
        var productId = message.ProductId ?? "products/" + message.Profile;
        var trial = Session.Query<Trial>()
            .Where(x => x.Email == message.Email && x.ProductId == productId)

        if (trial == null)
        trial.EndsAt = DateTime.Today.AddDays(message.Days);
        Bus.Send(new NewTrial
            ProductId = productId,
            Email = trial.Email,
            Company = trial.Company,
            FullName = trial.Name,
            TrackingId = trial.TrackingId

How do we test something like this? As it turns out, quite easily:

public class TrailTesting : ConsumersTests
    protected override void PrepareData(IDocumentSession session)
        session.Store(new Trial
            Email = "you@there.gov",
            EndsAt = DateTime.Today,
            ProductId = "products/nhprof"

    public void Will_update_trial_date()
        Consume<ExtendTrialLicenseConsumer, ExtendTrialLicense>(new ExtendTrialLicense
            ProductId = "products/nhprof",
            Days = 30,
            Email = "you@there.gov",

        using (var session = documentStore.OpenSession())
            var trial = session.Load<Trial>(1);
            Assert.Equal(DateTime.Today.AddDays(30), trial.EndsAt);

    // more tests here

All the magic happens in the base class, though:

public abstract class ConsumersTests : IDisposable
    protected IDocumentStore documentStore;
    private IServiceBus Bus = new FakeBus();

    protected ConsumersTests()
        documentStore = new EmbeddableDocumentStore
            RunInMemory = true,
            Conventions =
                    DefaultQueryingConsistency = ConsistencyOptions.QueryYourWrites

        IndexCreation.CreateIndexes(typeof(Products_Stats).Assembly, documentStore);

        using (var session = documentStore.OpenSession())

    protected T ConsumeSentMessage<T>()
        var fakeBus = ((FakeBus)Bus);
        object o = fakeBus.Messages.Where(x => x.GetType() == typeof(T)).First();

        return (T) o;

    protected void Consume<TConsumer, TMsg>(TMsg msg)
        where TConsumer : ConsumerOf<TMsg>, new()
        var foo = new TConsumer();

        using (var documentSession = documentStore.OpenSession())
            Set(foo, documentSession);
            Set(foo, Bus);
            Set(foo, documentStore);



    private void Set<T,TValue>(T foo, TValue value)
        PropertyInfo firstOrDefault = typeof(T).GetProperties().FirstOrDefault(x=>x.PropertyType==typeof(TValue));
        if (firstOrDefault == null) return;
        firstOrDefault.SetValue(foo, value, null);

    protected virtual void PrepareData(IDocumentSession session)

    public void Dispose()

And here are the relevant details for the FakeBus implementation:

public class FakeBus : IServiceBus
    public List<object>  Messages = new List<object>();

    public void Send(params object[] messages)

Now, admittedly, this is a fairly raw approach and we can probably do better. This is basically hand crafted auto mocking for consumers, and I don’t like the Consume<TConsumer,TMsg>() syntax very much. But it works, it is simple and it doesn’t really gets in the way.

I won’t say it is the way to go about it, but it is certainly easier than many other efforts that I have seen. We just need to handle the inputs & outputs and have a way to look at the local state, and you are pretty much done.


Comment preview

Comments have been closed on this topic.


  1. RavenDB Conference 2016–Slides - 17 hours from now
  2. Proposed solution to the low level interview question - about one day from now

There are posts all the way to Jun 02, 2016


  1. The design of RavenDB 4.0 (14):
    26 May 2016 - The client side
  2. RavenDB 3.5 whirl wind tour (14):
    25 May 2016 - Got anything to declare, ya smuggler?
  3. Tasks for the new comer (2):
    15 Apr 2016 - Quartz.NET with RavenDB
  4. Code through the looking glass (5):
    18 Mar 2016 - And a linear search to rule them
  5. Find the bug (8):
    29 Feb 2016 - When you can't rely on your own identity
View all series



Main feed Feed Stats
Comments feed   Comments Feed Stats