启用secondLevelRetriesEnabled时如何单元测试Rebus的IFailed<Message>处理器
解决方案
你遇到的报错本质是直接手动调用处理器Handle方法时,没有初始化Rebus依赖的消息上下文。真实运行时Rebus分发消息会自动将当前消息上下文绑定到异步本地存储,而单元测试里直接调用Handle时缺少这一步,导致bus.Advanced.TransportMessage.Deadletter找不到对应的上下文信息。
方法1:使用FakeMessageContext手动绑定上下文(推荐)
Rebus.Testing 包内置了FakeMessageContext类,可以快速模拟消息上下文,只需要在调用处理器方法前将上下文绑定到当前异步环境即可。
示例代码:
// 1. 构造测试用的IFailed<TMessage>实例 var ordinaryMessage = new OrdinaryMessage(); var failedMessage = new Failed<OrdinaryMessage>( message: ordinaryMessage, errorDescription: "Failed to handle message after 2 deferrals", numberOfDeliveryAttempts: 2 ); // 2. 初始化FakeBus和你的处理器 var fakeBus = new FakeBus(); var handler = new Handler<OrdinaryMessage>(fakeBus); // 3. 绑定FakeMessageContext到当前异步上下文,using块内的代码都能读取到该上下文 using var _ = new FakeMessageContext(failedMessage); // 4. 调用处理器的IFailed消息处理方法 await handler.Handle(failedMessage); // 5. 断言死信逻辑符合预期 var deadlettered = fakeBus.Advanced.DeadletteredMessages.Single(); Assert.Equal(ordinaryMessage, deadlettered.Message);
方法2:封装自定义测试夹具(复用度更高)
如果需要经常测试失败消息处理逻辑,可以自行封装简易的测试夹具,模拟你想要的DeliverFailed能力,示例封装逻辑:
public class HandlerFixture<THandler, TMessage> where THandler : IHandleMessages<IFailed<TMessage>> { public FakeBus Bus { get; } = new FakeBus(); public THandler Handler { get; } public HandlerFixture(THandler handler) { Handler = handler; } public async Task DeliverFailed(TMessage message, Exception exception, int deliveryAttempts = 2) { var failedMessage = new Failed<TMessage>( message: message, errorDescription: exception.Message, numberOfDeliveryAttempts: deliveryAttempts ); using var _ = new FakeMessageContext(failedMessage); await Handler.Handle(failedMessage); } }
使用方式和你期望的一致:
var fixture = new HandlerFixture<Handler<OrdinaryMessage>, OrdinaryMessage>(new Handler<OrdinaryMessage>(new FakeBus())); await fixture.DeliverFailed(new OrdinaryMessage(), new ApplicationException("oh no")); // 后续断言fixture.Bus的死信数据即可
内容的提问来源于stack exchange,提问作者Y0rk1e
相关产品推荐
相关产品推荐

