rebus-org / Rebus

:bus: Simple and lean service bus implementation for .NET
https://mookid.dk/category/rebus
Other
2.31k stars 361 forks source link

Unit Testing IFailed Messages #644

Closed rsnj closed 5 years ago

rsnj commented 7 years ago

How can you unit test IFailed<> messages from the SagaFixture? I’ve tried creating a MockFailed class that implements IFailed and pass that to the Deliver method of the SagaFixture but it does not get handled by my Saga. The messages are Correlated correctly and they work using the SimpleRetryStrategy, but don’t work with the SagaFixture.

mookid8000 commented 7 years ago

Good question 🤔 let me take a look

mookid8000 commented 7 years ago

I took a look, and I realized that the reason why IFailed<T> cannot readily be delivered via the saga fixture thing, is that messages delivered this was are actually sent – just like real messages.

In this regard, the saga fixture is very realistic – it is a fully configured Rebus endpoint, only running with the in-mem transport, and some special hooks into the in-mem saga storage.

Simulating a second level retry would require some alternative way of delivering the message.... I will have to think about this some more

mookid8000 commented 7 years ago

I am still thinking... sorry for taking so long 😐

mookid8000 commented 6 years ago

I think faking an IFailed<> with the saga fixture requires some special tricks.

One way I can think of that might work is to attach a special header to the IFailed<> message when sending it, which can then be recognized in the incoming messages pipeline in a piece of middleware, which in turn can somehow bring the error tracker into the correct state, faking it as if the message has already had all of its 1st level delivery attempts carried out.

Requires more thinking :) I am changing this issue into a task:

rsnj commented 6 years ago

Would it be possible to add a method to the SagaFixture to pass the message and Exception to? That method would then be responsible for adding the plumbing to the message to enable it to be delivered as an IFailed message.

mookid8000 commented 6 years ago

Something like that, yes 😄

oliverhanappi commented 5 years ago

This may be somewhat related to #663, where I propose an integration testing facility for complex mechanisms like second level retries, deferrals, custom pipeline steps and the like.

mookid8000 commented 5 years ago

Fixed in Rebus.TestHelpers 5.1.0 – now you can

fixture.DeliverFailed(new OrdinaryMessage(...), new ApplicationException("oh no"));

and then the message will be immediately dispatched as an IFailed<OrdinaryMessage>.

Please note that handling IFailed<T> has the same requirements as an ordinary message on sagas, so you need to decide whether to IHandleMessages<IFailed<Yourmessage>> or IAmInitiatedBy<IFailed<Yourmessage>>.

Moreover, you need to

protected override void CorrelateMessages(ICorrelationConfig<YourSaga> config)
{
    config.Correlate<IFailed<Yourmessage>>(m => m.Message.SomeField, d => d.SomethingOnYourSaga);
}

Let me know if it works out 😄