Skip to content

Unit testing with Polly

martincostello edited this page Sep 28, 2023 · 29 revisions

Unit testing with Polly

ℹ️ This documentation describes the previous Polly v7 API. If you are using the new v8 API, please refer to pollydocs.org.

This page is a concise conceptual overview of different unit-testing approaches you may take with Polly. This page also exists in a longer version with worked examples

How to approach unit-testing code wrapped in Polly policies depends what you are aiming to test

[1] I want to unit-test what my code does, independent of Polly policies I apply

TL:DR; Polly's NoOpPolicy allows you to stub out Polly, to test your code as if Polly were not in the mix. Use DI to provide policies to consuming classes; tests can then stub out Polly by injecting NoOpPolicy in place of real policies.

A common need is to test the logic of your system-under-test as if Polly were not part of the mix.

Perhaps you have code modules for which you already had unit tests, including success and failure cases. You then retro-fit Polly for resilience. How does having the Polly policy in play affect your existing unit tests? Do all the tests need adjusting? How do I test what my code does without Polly 'interfering'?

Suggested strategy: stub out Polly for the purposes of those tests. This can be facilitated by using dependency injection to pass policies into code. That could be with a full DI container, or just simple constructor injection or property injection, per preference.

Polly defines a NoOpPolicy for this scenario. NoOpPolicy does nothing but execute the governed delegate as if Polly was not involved. In your tests, inject NoOpPolicy rather than the policies you use in production, and Polly is stubbed out of those tests.

For examples taking this concept further with PolicyRegistry or a policy factory, see our Unit testing with examples page.

[2] I want to unit-test that I've configured Polly policies to achieve my desired resilience strategy

TL;DR: Configure a mock of the underlying system to return faults the policies should handle. Can be useful as a specification for, and regression check on, the faults you intend to handle.

This angle on testing aims to check you've configured policies to match your desired resilience behaviour. If somebody changes the configuration, the test provides regression value by failing. The test can be read as a specification of the resilience behaviour for that piece of code.

In this testing approach, you typically stub or mock out the underlying systems called (for instance you might stub out a call to some endpoint to return TimeoutException), then check your configured policy does handle that.

[3] I want to unit-test how my code reacts to results or faults returned by the execution through Polly

TL;DR Mock your policies to return or throw particular outcomes, to test how your code responds.

You may want to test how your code reacts to results or faults returned by an execution through Polly. For instance, you may want to test how your code reacts if, despite resilience strategies, the execution eventually fails.

To do this, it can be helpful to mock your Polly policy to return particular results or throw particular outcomes on execution.

Polly policies all fulfil execution interfaces (ISyncPolicy, ISyncPolicy<TResult>, IAsyncPolicy and IAsyncPolicy<TResult>). These interfaces describe the .Execute/Async() overloads available on policies.

You can configure these methods on a mock policy, to return or throw faults you want to simulate.

PolicyResult and PolicyResult<TResult> have public factory methods, allowing you to mock .ExecuteAndCapture(...) overloads to return the PolicyResult of your choice.

[4] I want to unit-test that the Polly policies I apply actually do what they say on the tin

TL:DR; Bear in mind the Polly codebase already tests this for you extensively.

An understandable desire when introducing Polly to a project is to want to check the Polly policy does what it says on the tin. If I configure Policy.Handle<FooException>().Retry(3), it would be nice to check it really works, right?

Writing unit-tests to verify that Polly works can be a very valuable way to explore and understand what Polly does. But, to allow you to concentrate on delivering your business value rather than reinventing Polly's test wheel, keep in mind that the Polly codebase tests its own operation extensively. (As at Polly v6.0, the Polly codebase has around 1700 tests per target framework.)

You can also explore and run the Polly-samples to see policies working individually, and in combination.

If you write your own integration tests around policies in your project, note the possibility to manipulate Polly's abstracted SystemClock. Where a test would usually incur a delay (for example, waiting the time for a circuit-breaker to transition from open to half-open state), manipulating the abstracted clock can avoid real-time delays. For insight into how to do this, pull down the codebase and check out how Polly's own unit tests manipulate the clock.

Questions?

Thoughts/questions about unit-testing? Post an issue on the issues board.

Clone this wiki locally