Unit Test Typed HttpClient with Microsoft.Extensions.Http.Resilience

288 Views Asked by At

I am getting to know Microsoft.Extensions.Http.Resilience and have configured very basic pipeline using dependency injection per the docs:

using IHost host = Host.CreateDefaultBuilder(args)
    .CondifureServices(services => 
    {
        services.AddHttpClient<IMyClient, MyClient>()
        .AddStandardResilienceHandler()
    })
    .Build();

I want to unit test MyClient with the resilience but not sure how to set it up. I have this so far but I would expect I would be able to mimic Program.cs and use AddStandardResilienceHandler() to pipeline.

IMyClient sut = new MyClient(new HttpClient());

ResiliencePipeline<int> pipeline = new ResiliencePipelineBuilder<int>()
    .AddRetry(new RetryStrategyOptions<int>())  // <= no AddStandardResilienceHandler method
    .Build();

await pipeline.ExecuteAsync<int>(async token =>
{
    var data = await sut.GetData();
    return data;
});
1

There are 1 best solutions below

2
On BEST ANSWER

The short answer is that you can't do that in unit test. The decoration of the MyClient with the resilience pipeline is done via the DI engine. So, you can't test that in your unit test.

I would suggest to unit test your MyClient implementation without any resilience pipeline and create some integration tests to see how the MyClient and the standard resilience handler works together.


By the way the AddStandardResilienceHandler registers several strategies, not just a retry

builder
  .AddRateLimiter(options.RateLimiter)
  .AddTimeout(options.TotalRequestTimeout)
  .AddRetry(options.Retry)
  .AddCircuitBreaker(options.CircuitBreaker)
  .AddTimeout(options.AttemptTimeout)

Source

where the options is a HttpStandardResilienceOptions instance

public HttpRateLimiterStrategyOptions RateLimiter { get; set; } = new HttpRateLimiterStrategyOptions
{
    Name = StandardPipelineNames.RateLimiter
};

...
public HttpTimeoutStrategyOptions AttemptTimeout { get; set; } = new()
{
    Timeout = TimeSpan.FromSeconds(10),
    Name = StandardPipelineNames.AttemptTimeout
};

Source

These Http prefixed options classes are inherited from the standard Polly options. Some of them do not overwrite any default values (like HttpTimeoutStrategyOptions) whereas others do (for example: HttpRetryStrategyOptions).

My whole point with this: do not try to recreate the same pipeline in your unit / integration test.


UPDATE #1

say in Retry, I might add my own callback I want to be able to verify its behaviour. I guess I could create a ServiceCollection in my unit test and run tests against an instance provided by that DI container

If you want to test your callback's functionality then do not inline it rather than extract it into a dedicated class. That allows you to write tests separately for it.

You don't need to test whether your callback will be called or not. Polly has covered these use cases and you don't need to check whether Polly is doing its job or not.