Skip to content

Create a unit test

MassTransit is an asynchronous framework that enables the development of high-performance and flexible distributed applications. Because of MassTransit’s asynchronous underpinning, unit testing consumers, sagas, and routing slip activities can be significantly more complex. To simplify the creation of unit and integration tests, MassTransit includes a Test Harness that simplifies test creation.

  • Simplifies configuration for a majority of unit test scenarios
  • Provides an in-memory transport, saga repository, and message scheduler
  • Exposes published, sent, and consumed messages
  • Supports Web Application Factory for testing ASP.NET Applications

As stated above, MassTransit is an asynchronous framework. In most cases, developers want to test that message consumption is successful, consumer behavior is as expected, and messages are published and/or sent. Because these actions are performed asynchronously, MassTransit’s test harness exposes several asynchronous collections allowing test assertions verifying developer expectations. These asynchronous collections are backed by an over test timer and an inactivity timer, so it’s important to use a test harness only once for a given scenario. Multiple test assertions, messages, and behaviors are normal in a given test, but unrelated scenarios should not share a single test harness.

Unit testing is a good practice to ensure that your code works as expected, and MassTransit’s test harness makes it easy to test your consumers (including consumers, job consumers, saga state machines, and routing slip activities).

A unit test uses the Microsoft.Extensions.DependencyInjection container to configure the test harness. Using the ServiceCollection extension method AddMassTransitTestHarness adds the test harness to the container. The test harness is then started using the StartTestHarness method. When the ServiceProvider returned by BuildServiceProvider is disposed, the test harness is automatically stopped.

[Test]
public async Task A_complete_unit_test()
{
await using var provider = new ServiceCollection()
.AddMassTransitTestHarness(x =>
{
})
.BuildServiceProvider(true);
var harness = await provider.StartTestHarness();
}

The unit test above is the minimum required to get started with the test harness. It assumes that the in-memory transport is used, the delayed message scheduler is configured, and automatically calls ConfigureEndpoints on the bus to configure the receive endpoints

A more verbose example is shown below.

[Test]
public async Task A_complete_unit_test()
{
await using var provider = new ServiceCollection()
.AddMassTransitTestHarness(x =>
{
x.AddDelayedMessageScheduler();
x.UsingInMemory((context, cfg) =>
{
cfg.UseDelayedMessageScheduler();
cfg.ConfigureEndpoints(context));
});
})
.BuildServiceProvider(true);
var harness = await provider.StartTestHarness();
}

In addition to the default in-memory transport, the test harness can be used with any supported transport. This might be useful for testing using an actual message transport in an integration test where a transport-specific feature is required.

For example, to use Azure Service Bus as the transport and test a subscription endpoint, add the following to the AddMassTransitTestHarness method.

[Test]
public async Task An_azure_service_bus_unit_test()
{
await using var provider = new ServiceCollection()
.AddMassTransitTestHarness(x =>
{
x.AddServiceBusMessageScheduler();
x.AddConsumer<AuditOrderCreatedConsumer>();
x.UsingAzureServiceBus((context, cfg) =>
{
cfg.Host("sb:///my-namespace.servicebus.windows.net"));
cfg.UseServiceBusMessageScheduler();
cfg.SubscriptionEndpoint<OrderCreated>("audit-order-created", e =>
{
e.ConfigureConsumer<AuditOrderCreatedConsumer>(context);
});
cfg.ConfigureEndpoints(context));
});
})
.BuildServiceProvider(true);
var harness = await provider.StartTestHarness();
await harness.Scope.ServiceProvider.GetRequiredService<IPublishEndpoint>().Publish(new OrderCreated());
Assert.That(await harness.Consumed.Any<OrderCreated>());
}

To verify that messages were sent, published, or consumed, you can use the Select or SelectAsync methods on the Sent, Published, or Consumed collections.

var messageSent = await harness.Sent.SelectAsync<OrderSubmitted>()
.FirstOrDefault();
Assert.AreEqual(orderId, messageSent?.Context.Message.OrderId);

In the example above, the await harness.Sent.Any<OrderSubmitted>() method call waits for the OrderSubmitted message to be sent within the time specified by the TestInactivityTimeout property of the Test Harness. The default timeout is 30 seconds, and 50 minutes while debugging, configured by the TestHarnessOptions, and can be set:

To set both the overall test timeout and the test inactivity timeout, call the SetTestTimeouts method:

cfg.SetTestTimeouts(TimeSpan.FromSeconds(60), TimeSpan.FromSeconds(5)));

Either timeout value can be set individually using the appropriately named parameter:

cfg.SetTestTimeouts(testTimeout: TimeSpan.FromSeconds(60));
cfg.SetTestTimeouts(testInactivityTimeout: TimeSpan.FromSeconds(5));