An in-memory messaging transport for tests. It runs your consumers with no broker and captures every produced
and consumed message for assertion. Built entirely on the public Vulthil.Messaging.Transport SDK, so it
assembles the same execution plans (consumers, polymorphic dispatch, per-consumer retry resolution) from your
queue configuration that a real transport would.
- Component/unit tests that assert published, sent, consumed, or requested messages
- Integration tests that exercise the production composition without a broker
- Standing in for an external service (mock a request reply or a downstream event handler)
- Dispatch is synchronous: when a publish/send/request call returns, every consumer it triggered has run — no polling
- A one-way consumer's exception does not propagate to the publisher/sender: the consumer is retried per its
resolved retry policy (attempts run back-to-back, without the configured delays), then a
Fault<T>is published and captured — assert it viaPublished<Fault<TMessage>>(). A request consumer's exception becomes a failedResult<TResponse>on the requesting side - Keep assertions on
ITestHarnessdeterministic and explicit;Clear()between phases
var builder = Host.CreateApplicationBuilder();
builder.AddMessaging(messaging =>
{
messaging.ConfigureQueue("orders", q => q.AddConsumer<OrderCreatedConsumer>());
messaging.UseTestHarness();
});
using var host = builder.Build();
var publisher = host.Services.GetRequiredService<IPublisher>();
var harness = host.Services.GetRequiredService<ITestHarness>();
await publisher.PublishAsync(new OrderCreatedEvent(orderId));
harness.Published<OrderCreatedEvent>().ShouldHaveSingleItem().Message.OrderId.ShouldBe(orderId);
harness.Consumed<OrderCreatedEvent>().ShouldHaveSingleItem();// Answer a request as an external service would (takes precedence over a real request consumer):
harness.Respond<GetWeatherRequest, WeatherForecast>(ctx => new WeatherForecast(ctx.Message.City, 20));
// React to a published/sent message as a fake downstream service:
harness.Handle<OrderShippedEvent>(ctx => { observed.Add(ctx.Message.OrderId); return Task.CompletedTask; });Call ReplaceTransportWithTestHarness() from a test host's service hook to replace the registered broker
transport with the harness, leaving production code untouched:
builder.ConfigureServices(services => services.ReplaceTransportWithTestHarness());See the Testing guide for the full API and details.