Getting Started
Architecture
NServiceBus
Transports
Persistence
ServiceInsight
ServicePulse
ServiceControl
Monitoring

Unit Testing a custom recoverability policy

Component: NServiceBus
NuGet Package: NServiceBus (8.x)

This sample demonstrates how to test custom recoverability policies. The sample uses the policy as explained in the custom recoverability policy documentation.

Creating the policy

The syntax of a custom recoverability policy is following:

RecoverabilityAction MyCustomRetryPolicy(RecoverabilityConfig config, ErrorContext context)

This means that an instance of RecoverabilityConfig is necessary in order to test the custom policy. This class contains information about immediate and delayed retries and about what happens when all retries have been exhausted. Normally this information is configured with an EndpointConfiguration and provided by NServiceBus to the recoverability policy. In the test context it needs to be build based on the test parameters. This can be done using the following helper method:

static Func<ErrorContext, RecoverabilityAction> CreatePolicy(int maxImmediateRetries = 2, int maxDelayedRetries = 2, TimeSpan? delayedRetryDelay = null, HashSet<Type> unrecoverableExceptions = null)
{
    var failedConfig = new FailedConfig("errorQueue", unrecoverableExceptions ?? new HashSet<Type>());
    var config = new RecoverabilityConfig(new ImmediateConfig(maxImmediateRetries), new DelayedConfig(maxDelayedRetries, delayedRetryDelay.GetValueOrDefault(TimeSpan.FromSeconds(2))), failedConfig);
    return context => CustomizedRecoverabilityPolicy.MyCustomRetryPolicy(config, context);
}

Executing the policy requires an error context, which provides information about the error that occurred and how many retries have already been executed. This sample also includes a helper method that creates the error context.

static ErrorContext CreateErrorContext(int numberOfDeliveryAttempts = 1, int? retryNumber = null, Dictionary<string, string> headers = null, Exception exception = null) =>
    new ErrorContext(
        exception ?? new Exception(),
        retryNumber.HasValue
            ? new Dictionary<string, string> { { Headers.DelayedRetries, retryNumber.ToString() } }
            : headers ?? new Dictionary<string, string>(),
        "message-id",
        new ReadOnlyMemory<byte>(new byte[0]),
        new TransportTransaction(),
        numberOfDeliveryAttempts,
        "receive-address",
        new ContextBag());

Creating a test

Multiple tests can then be created using the helper methods. According to the custom recoverability policy, messages that cause a certain custom exception should be discarded and not moved to the error queue.

[Test]
public void ShouldDiscardBecauseOfCustomException()
{
    var policy = CreatePolicy();
    var errorContext = CreateErrorContext(numberOfDeliveryAttempts: 1, exception: new MyBusinessTimedOutException());

    var recoverabilityAction = policy(errorContext);

    Assert.That(recoverabilityAction, Is.InstanceOf<Discard>(), "Message should be discarded");
}

There are additional tests in the sample that show how it is possible to verify which error queue a message was sent to.

Related Articles

  • Custom Recoverability Policy
    Shows how to take full control over Recoverability by implementing a Recoverability Policy.
  • Recoverability
    Explains how exceptions are handled, and actions retried, during message processing.
  • Testing NServiceBus
    Develop service layers and long-running processes using test-driven development.