Search code examples
unit-testingrebussaga

Unit testing of Saga handlers in rebus and correlation issues


I have this simple Saga in Rebus:

public void MySaga : Saga<MySagaData>
    IAmInitiatedBy<Event1>
    IHandleMessages<Event2>
{
        private IBus bus;
        private ILog logger;

        public MySaga(IBus bus, ILog logger)
        {
            if (bus == null) throw new ArgumentNullException("bus");
            if (logger == null) throw new ArgumentNullException("logger");

            this.bus = bus;
            this.logger = logger;
        }

        protected override void CorrelateMessages(ICorrelationConfig<MySagaData> config)
        {

            config.Correlate<Event>(m => m.MyObjectId.Id, s => s.Id);
            config.Correlate<Event>(m => m.MyObjectId.Id, s => s.Id);
        }

    public Task Handle(Event1 message)
    {
        return Task.Run(() =>
        {
            this.Data.Id = message.MyObjectId.Id;
            this.Data.State = MyEnumSagaData.Step1;
            var cmd = new ResponseCommandToEvent1(message.MyObjectId);
            bus.Send(cmd);
        });
    }

    public Task Handle(Event2 message)
    {
        return Task.Run(() =>
        {
            this.Data.State = MyEnumSagaData.Step2;
            var cmd = new ResponseCommandToEvent2(message.MyObjectId);
            bus.Send(cmd);
        });
    }
}

and thanks to the kind mookid8000 I can test the saga using FakeBus and a SagaFixture:

[TestInitialize]
public void TestInitialize()
{
    var log = new Mock<ILog>();
    bus = new FakeBus();
    fixture = SagaFixture.For<MySaga>(() => new MySaga(bus, log.Object));
    idTest = new MyObjectId(Guid.Parse("1B2E7286-97E5-4978-B5B0-D288D71AD670"));
}

[TestMethod]
public void TestIAmInitiatedBy()
{
    evt = new Event1(idTest);
    fixture.Deliver(evt);
    var testableFixture = fixture.Data.OfType<MySagaData>().First();
    Assert.AreEqual(MyEnumSagaData.Step1, testableFixture.State);
    // ... more asserts
}

[TestMethod]
public void TestIHandleMessages()
{
    evt = new Event2(idTest);
    fixture.Deliver(evt);
    var testableFixture = fixture.Data.OfType<MySagaData>().First();
    Assert.AreEqual(MyEnumSagaData.Step2, testableFixture.State);
    // ... more asserts
}

[TestCleanup]
public void TestCleanup()
{
    fixture.Dispose();
    bus.Dispose();
}

The first test method that check IAmInitiatedBy is correctly executed and no error is thrown, while the second test fail. It looks like a correlation issues since fixture.Data contains no elements and in fixture.LogEvents contains as last elements this error: Could not find existing saga data for message Event2/b91d161b-eb1b-419d-9576-2c13cd9d9c51.

What is this GUID? Is completly different from the one I defined in the unit test? Any ideas? Is legal what I'm tryng to test (since I'm using an in-memory bus)?


Solution

  • This line is bad: this.Data.Id = message.MyObjectId.Id. If you checked the value of Data.Id before you overwrote it, you would have noticed that the property already had a value.

    You do not assign the saga ID - Rebus does that. And you should leave that property alone :)

    Regarding your error - when Rebus wants to log information about a specific message, it logs a short name for the type and the message ID, i.e. the value of the automatically-assigned rbs2-msg-id header. In other words: It's not the value of the property m.MyObjectId.Id, you're seeing, it's the message ID.

    Since the saga fixture is re-initialized for every test run, and you only deliver an Event2 to it (which is not allowed to initiate a new instance), the saga will not be hit.