Home > Software engineering >  Why is Polly not retrying the exception in my unit test?
Why is Polly not retrying the exception in my unit test?

Time:10-01

I have the following method which has a retriable network call. The retry policy is specified for a specific exception.

public async Task<MyResponse> GetRecords(MyRequest request)
{
    try
    {
        RetryPolicy retryPolicy = Policy.Handle<MyException>(ex => ex.ErrorCode == ErrorCode.MySpecificErrorCode)
            .Retry(MAX_RETRY_COUNT, onRetry: (exception, retryCount) =>
            {
                log($"Retrying for {retryCount} times due to my error that I want to retry");
            });
        return await retryPolicy.Execute(async () =>
                await OtherService.NetworkCall(request).ConfigureAwait(false))
                .ConfigureAwait(false);
    }
    catch (Exception ex)
    {
        log(ex);
        throw;
    }
}

This is the unit test.

[TestMethod()]
public async Task TestRetry()
{
    OtherServiceMock.SetupSequence(x => x.NetworkCall(It.IsAny<MyRequest>()))
        .ThrowsAsync(new MyException(ErrorCode.MySpecificErrorCode, ExceptionMessage.MySpecificErrorMessage)) //this is getting thrown 
        .ThrowsAsync(new Exception());

    MyRequest request = new MyRequest();
    
    try
    {
        var response = await new MyClassMock.GetRecords(request).ConfigureAwait(false);
    }
    catch(Exception ex)
    {
        log("Event","Event");
    }
}

The first exception is getting thrown instead of the second one. I have tweaked the max retry number, it didn't help. What am I doing wrong here?

CodePudding user response:

Just to make it clear, if you want to decorate a sync function with retry then you have to use one of the following policy builder methods:

  • Retry,
  • RetryForever,
  • WaitAndRetry,
  • WaitAndRetryForever

In this case the policy will be a RetryPolicy, which implements the ISyncPolicy interface. So, you can call the Execute which is NOT awaitable.

If you want to decorate an async function with retry then you have to use one of the following policy builder methods:

  • RetryAsync,
  • RetryForeverAsync,
  • WaitAndRetryAsync,
  • WaitAndRetryForeverAsync

In this case the policy will be a AsyncRetryPolicy, which implements the IAsyncPolicy interface. So, you can call the ExecuteAsync which is awaitable.


Here I have detailed how to choose between these method variants: https://stackoverflow.com/a/73095879/13268855

CodePudding user response:

Your mock setup is for x.GetRecord

OtherServiceMock.SetupSequence(x => x.GetRecord(It.IsAny<MyRequest>()))
    .ThrowsAsync(new MyException(ErrorCode.MySpecificErrorCode, ExceptionMessage.MySpecificErrorMessage)) //this is getting thrown 
    .ThrowsAsync(new Exception());

But the part you want polly to retry on is OtherService.NetworkCall

return await retryPolicy.Execute(async () =>
                    await OtherService.NetworkCall(request).ConfigureAwait(false))
                    .ConfigureAwait(false);

Your SetupSequence should be mocking NetworkCall

OtherServiceMock.SetupSequence(x => x.NetworkCall(It.IsAny<MyRequest>()))
    .ThrowsAsync(new MyException(ErrorCode.MySpecificErrorCode, ExceptionMessage.MySpecificErrorMessage)) //this is getting thrown 
    .ThrowsAsync(new Exception());
  • Related