Course contentsShow
← C#
Lesson 1685 of 1,90815. Software Quality and TestingFree lesson

Testing Polly Resilience Policies

Verify that retry and circuit breaker logic behaves as expected under failure conditions.

In this lesson, you will learn how to verify that your Polly retry and circuit breaker strategies actually intercept failures and protect your system as intended.

When we implement resilience policies, we often assume they work, but "silent" policy misconfigurations can lead to cascading failures. To test these, we must move beyond unit testing logic and into deterministic failure injection. We need to force our dependencies (like an HttpClient) to fail on cue so we can observe how our policies respond.

Verifying Retries

To test a retry policy, you don't want to wait for actual network timeouts. Instead, use a DelegatingHandler or a library like Moq to force the first N requests to return a 503 Service Unavailable, then return a 200 OK.

int callCount = 0;
var handlerMock = new Mock<HttpMessageHandler>();
handlerMock.Protected()
    .Setup<Task<HttpResponseMessage>>("SendAsync", ItExpr.IsAny<HttpRequestMessage>(), ItExpr.IsAny<CancellationToken>())
    .ReturnsAsync(() => {
        callCount++;
        return callCount < 3 
            ? new HttpResponseMessage(HttpStatusCode.ServiceUnavailable) 
            : new HttpResponseMessage(HttpStatusCode.OK);
    });

By asserting that callCount equals 3, you confirm that your retry policy intercepted the failures and attempted the operation exactly as configured.

Verifying the Circuit Breaker

The circuit breaker is trickier because it relies on state. Testing this requires two phases:

  1. The Trip: Trigger the failure threshold (e.g., three consecutive errors) and assert that the subsequent call throws BrokenCircuitException immediately, without even hitting the underlying mock.
  2. The Recovery: Use a Task.Delay or a time-manipulation library to wait for the breakDuration, then verify that the next request succeeds and resets the circuit.

Don't use real System.Threading.Thread.Sleep in these tests. Instead, inject an ITimeProvider (available in .NET 8+) into your policies so your tests can "fast-forward" time, allowing your circuit breaker tests to complete in milliseconds while simulating long waiting periods.

Key Takeaway: Never test resilience by waiting for real-world failures; use mocked handlers to inject deterministic errors and verify the policy's state transitions.