Testing
This document explains how to test Polly's resilience pipelines. You should not test how the resilience pipelines operate internally, but rather test your own settings or custom delegates.
To make the testing process simpler, Polly offers the Polly.Testing
package. This package has a range of APIs designed to help you test the setup and combination of resilience pipelines in your user code.
Usage
Begin by adding the Polly.Testing
package to your test project:
dotnet add package Polly.Testing
Use the GetPipelineDescriptor
extension method to get the ResiliencePipelineDescriptor
which provides details on the pipeline's composition:
// Build your resilience pipeline.
ResiliencePipeline pipeline = new ResiliencePipelineBuilder()
.AddRetry(new RetryStrategyOptions
{
MaxRetryAttempts = 4
})
.AddTimeout(TimeSpan.FromSeconds(1))
.Build();
// Retrieve the descriptor.
ResiliencePipelineDescriptor descriptor = pipeline.GetPipelineDescriptor();
// Check the pipeline's composition with the descriptor.
Assert.Equal(2, descriptor.Strategies.Count);
// Verify the retry settings.
var retryOptions = Assert.IsType<RetryStrategyOptions>(descriptor.Strategies[0].Options);
Assert.Equal(4, retryOptions.MaxRetryAttempts);
// Confirm the timeout settings.
var timeoutOptions = Assert.IsType<TimeoutStrategyOptions>(descriptor.Strategies[1].Options);
Assert.Equal(TimeSpan.FromSeconds(1), timeoutOptions.Timeout);
The GetPipelineDescriptor
extension method is also available for the generic ResiliencePipeline<T>
:
// Construct your resilience pipeline.
ResiliencePipeline<string> pipeline = new ResiliencePipelineBuilder<string>()
.AddRetry(new RetryStrategyOptions<string>
{
MaxRetryAttempts = 4
})
.AddTimeout(TimeSpan.FromSeconds(1))
.Build();
// Obtain the descriptor.
ResiliencePipelineDescriptor descriptor = pipeline.GetPipelineDescriptor();
// Check the pipeline's composition with the descriptor.
// ...
Mocking ResiliencePipelineProvider<TKey>
Consider the following code that might resemble a part of your project:
// Represents an arbitrary API that needs resilience support
public class MyApi
{
private readonly ResiliencePipeline _pipeline;
// The value of pipelineProvider is injected via dependency injection
public MyApi(ResiliencePipelineProvider<string> pipelineProvider)
{
_pipeline = pipelineProvider.GetPipeline("my-pipeline");
}
public async Task ExecuteAsync(CancellationToken cancellationToken)
{
await _pipeline.ExecuteAsync(
static async token =>
{
// Add your code here
},
cancellationToken);
}
}
// Extensions to incorporate MyApi into dependency injection
public static class MyApiExtensions
{
public static IServiceCollection AddMyApi(this IServiceCollection services)
{
return services
.AddResiliencePipeline("my-pipeline", builder =>
{
builder.AddRetry(new RetryStrategyOptions
{
MaxRetryAttempts = 4
});
})
.AddSingleton<MyApi>();
}
}
In the example above:
- The
MyApi
class is introduced, representing part of your application that requires resilience support. - The
AddMyApi
extension method is also defined, which integratesMyApi
into dependency injection (DI) and sets up the resilience pipeline it uses.
For unit tests, if you want to assess the behavior of ExecuteAsync
, it might not be practical to rely on the entire pipeline, especially since it could slow down tests during failure scenario evaluations. Instead, it's recommended to mock the ResiliencePipelineProvider<string>
and return an empty pipeline:
ResiliencePipelineProvider<string> pipelineProvider = Substitute.For<ResiliencePipelineProvider<string>>();
// Mock the pipeline provider to return an empty pipeline for testing
pipelineProvider
.GetPipeline("my-pipeline")
.Returns(ResiliencePipeline.Empty);
// Use the mocked pipeline provider in your code
var api = new MyApi(pipelineProvider);
// You can now test the api
This example leverages the NSubstitute
library to mock the pipeline provider.