我想对我的MassTransit使用者进行单元测试,它不返回响应。目前,我的测试似乎发布了一条消息,但消费者没有被触发,所以我的断点根本没有被击中。
消费者是相当直截了当的,但它确实有通过DI注入的服务。
public class BudgetExceededConsumer : IConsumer<IBudgetExceeded>
{
private readonly INotificationHubService _notificationHubService;
public BudgetExceededConsumer(INotificationHubService notificationHubService)
{
_notificationHubService = notificationHubService;
}
public async Task Consume(ConsumeContext<IBudgetExceeded> context)
{
try
{
var message = context.Message;
await _notificationHubService.SendNotificationAsync(context.Message);
}
catch (Exception ex)
{
throw new Exception("Failed to send push notification for exceeding budget usage", ex);
}
}
}
使用以下方法将使用者添加到my函数中:
builder.Services.AddMassTransitForAzureFunctions(cfg =>
{
cfg.AddConsumersFromNamespaceContaining<ConsumerNamespace>();
});
我有一个相对简单的服务,其他函数使用它来发送消息:
private readonly ISendEndpointProvider _sendEndpoint;
public MessagingService(ISendEndpointProvider sendEndpoint)
{
_sendEndpoint = sendEndpoint;
}
public async Task SendMessage<T>(string queueName, object messageBody) where T : class, MessageBase
{
var endpoint = await _sendEndpoint.GetSendEndpoint(new Uri($"queue:{queueName}"));
await endpoint.Send<T>(messageBody);
}
我想为使用者编写一个简单的测试,这样我就可以模拟服务,然后验证模拟的服务是否被调用了。然而,我无法达到运行测试和我的消费者被断点击中的地步。我不会设置注入到DI中任何地方的使用者的服务。目前,这并不是抱怨,这让我觉得我错过了一些设置。
public async Task Budget_message_gets_consumed()
{
await using var provider = new ServiceCollection()
.AddMassTransitInMemoryTestHarness(cfg =>
{
cfg.AddConsumer<BudgetExceededConsumer>();
cfg.AddConsumerTestHarness<BudgetExceededConsumer>();
})
.BuildServiceProvider(true);
var harness = provider.GetRequiredService<InMemoryTestHarness>();
await harness.Start();
try
{
var bus = provider.GetRequiredService<IBus>();
BudgetExceededMessage message = new BudgetExceededMessage
{
UserEmailAddress = "test@email.com",
Budget = "£20.00",
TotalSpend = "£23.56"
};
await bus.Publish(message);
var result = await harness.Consumed.Any<IBudgetExceeded>();
Assert.That(result, Is.True); //This is true
var consumerHarness = provider.GetRequiredService<IConsumerTestHarness<BudgetExceededConsumer>>();
var result2 = await consumerHarness.Consumed.Any<IBudgetExceeded>();
Assert.That(result2, Is.True); //This is FALSE.
}
finally
{
await harness.Stop();
await provider.DisposeAsync();
}
}
如您所见,第二个断言是假的。我想,如果这是真的,我会看到我的消费者的断点受到打击。
这里是否存在需要更改的设置,以便正确评估第二个断言?我知道我的设置与文档略有不同,因为我没有使用给出响应的方法。
谢谢
发布于 2022-07-07 12:25:21
我或多或少地遇到了同样的问题,并最终使它在两个更改(第二个更改可选)中正确工作。
1.我的使用者使用的是一个指定的端点,所以我需要在线束中的使用者设置中配置这个端点。
要检查这是否是您的问题,您可以查看日志(注意第3行和第5行)。
08:00:18.424-D Starting bus: loopback://localhost/
08:00:18.440-D Endpoint Ready: loopback://localhost/mbp14_testhost_bus_gtwyyyrqpojbqfzhbdpgyrnng6
08:00:18.440-D Endpoint Ready: loopback://localhost/PublishPdp
08:00:18.442-I Bus started: loopback://localhost/
08:00:18.454-D Create send transport: loopback://localhost/publish-pdp?bind=true
08:00:18.507-D SEND loopback://localhost/publish-pdp?bind=true 34680000-8e6c-1217-6974-08da601042eb Ownit.Next.Common.Messages.PublishPdp
注意两个不同的地址:PublishPdp
和publish-pdp
。在测试工具中,添加以下代码:
EndpointConvention.Map<PublishPdp>(new Uri("queue:publish-pdp"));
还不够,我还必须更新使用者配置:
await using var provider = new ServiceCollection()
.AddMassTransitTestHarness(cfg =>
{
cfg.AddConsumer<PublishPdpConsumer>()
.Endpoint(e => e.Name = "publish-pdp"); // Explicitly set the name.
})
.AddScoped(x => Log.Logger)
.BuildServiceProvider(true);
或者使用默认命名:
// This also works by using the default naming scheme
EndpointConvention.Map<PublishPdp>(new Uri("queue:PublishPdp"));
await using var provider = new ServiceCollection()
.AddMassTransitTestHarness(cfg =>
{
cfg.AddConsumer<PublishPdpConsumer>(); // No naming needed
})
.AddScoped(x => Log.Logger)
.BuildServiceProvider(true);
我曾假设静态EndpointConvention.Map<>()
也会影响消费者,但事实并非如此。使用者甚至有一个静态抑制器,它显式地设置了这一点。
当您不使用默认名称时,测试用例必须具有显式的EndpointConvention.Map<>(new Url("queue:X"))
、和 AddConsumer<>().Endpoint(e => e.Name = "X")
。在测试工具中使用Send
时,仍然需要使用EndpointConvention.Map<>(new Url("queue:X"))
,其中X
是消息类型的名称。
AddConsumerTestHarness
2.显式添加似乎没有必要的。
有一些混淆,因为文档只显示两个示例,GitHub问题上可用的大多数示例都显示了“旧”样式的设置。
这个方法有一个ObsoleteAttribute
:
Consider migrating to AddMassTransitTestHarness, which does not require this extra configuration
实际上,它似乎没有必要,测试用例可以这样编写:
[Fact]
public async Task Publish_Pdp_Is_Consumed_With_DI()
{
// This mapping is needed in the test
EndpointConvention.Map<PublishPdp>(new Uri("queue:publish-pdp"));
await using var provider = new ServiceCollection()
.AddMassTransitTestHarness(cfg =>
{
// Need to explicitly name the endpoint. Commenting out one
// or both causes the test to fail.
cfg.AddConsumer<PublishPdpConsumer>()
.Endpoint(e => e.Name = "publish-pdp");
})
.AddScoped(x => Log.Logger) // I inject Serilog so I need this.
.BuildServiceProvider(true);
var harness = provider.GetRequiredService<ITestHarness>();
await harness.Start();
await harness.Bus.Send<PublishPdp>(new
{
Id = Guid.NewGuid(),
CreatedBy = "test_user"
});
Assert.True(await harness.Sent.Any<PublishPdp>());
Assert.True(await harness.Consumed.Any<PublishPdp>());
}
或
[Fact]
public async Task Publish_Pdp_Is_Consumed_With_DI()
{
// Map the default name
EndpointConvention.Map<PublishPdp>(new Uri("queue:PublishPdp"));
await using var provider = new ServiceCollection()
.AddMassTransitTestHarness(cfg =>
{
// Don't name the endpoint, but explicitly map the default
cfg.AddConsumer<PublishPdpConsumer>();
})
.AddScoped(x => Log.Logger) // I inject Serilog so I need this.
.BuildServiceProvider(true);
var harness = provider.GetRequiredService<ITestHarness>();
await harness.Start();
await harness.Bus.Send<PublishPdp>(new
{
Id = Guid.NewGuid(),
CreatedBy = "test_user"
});
Assert.True(await harness.Sent.Any<PublishPdp>());
Assert.True(await harness.Consumed.Any<PublishPdp>());
}
感谢克里斯的帮助。
https://stackoverflow.com/questions/72563757
复制相似问题