C# Polly AsyncPolicy的单元测试SleepDurationProvider方法 当前解决方案

C# Polly AsyncPolicy的单元测试SleepDurationProvider方法 当前解决方案,c#,api,unit-testing,httpclient,polly,C#,Api,Unit Testing,Httpclient,Polly,我有一个C#库,其中包含一个拥有多个API的主类,并以各种方式使用所有API。每个API都将其HttpClient.SendAsync包装在PollyAsyncPolicy.ExecuteAsync中。此异步策略是通过调用名为CreateRetryPolicy的主类中的方法创建的,该方法需要两个委托,sleepDurationProvider和onRetryAsync: public class MainClass { private readonly IFirstApi firstAp

我有一个C#库,其中包含一个拥有多个API的主类,并以各种方式使用所有API。每个API都将其
HttpClient.SendAsync
包装在Polly
AsyncPolicy.ExecuteAsync
中。此异步策略是通过调用名为
CreateRetryPolicy
的主类中的方法创建的,该方法需要两个委托,
sleepDurationProvider
onRetryAsync

public class MainClass
{
    private readonly IFirstApi firstApi;
    private readonly ISecondApi secondApi;

    private readonly HttpClient httpClient = new HttpClient();

    public MainClass()
    {
        firstApi = new FirstApi(httpClient, CreateRetryPolicy);
        secondApi = new SecondApi(httpClient, CreateRetryPolicy);
    }

    // Main class code that uses APIs

    public static AsyncPolicy<HttpResponseMessage> CreateRetryPolicy(
        Func<int, TimeSpan> sleepDurationProvider,
        Func<DelegateResult<HttpResponseMessage>, TimeSpan, Context, Task> onRetryAsync
        )
    {
        return Policy.HandleResult<HttpResponseMessage>(
            r => !r.IsSuccessStatusCode)
            .Or<TaskCanceledException>()
            .WaitAndRetryAsync(
                5,
                sleepDurationProvider: sleepDurationProvider,
                onRetryAsync: onRetryAsync
                );
    }
}
如您所见,
SleepDuration
方法返回一个
TimeSpan
,它随重试次数呈指数级扩展,默认情况下
RetryAction
方法不执行任何操作并返回已完成的任务。对于继承
BaseApi
的每个API,要创建重试策略,只需调用:

var newRetryPolicy = retryPolicy(SleepDuration, RetryAction);
问题 这个解决方案很好,但是我在单元测试时遇到了一个问题。对于主类功能,我只需使用接口模拟每个API。为了测试每个API功能,我创建了真实的API对象,但向其构造函数传递了一个不同的委托,该委托忽略了
sleepDurationProvider
,每次尝试之间只需等待一毫秒:

private readonly RetryPolicyDelegate retryPolicyDummy = (
    Func<int, TimeSpan> sleepDurationProvider,
    Func<DelegateResult<HttpResponseMessage>, TimeSpan, Context, Task> onRetryAsync
) =>
{
    return Policy.HandleResult<HttpResponseMessage>(
            r => !r.IsSuccessStatusCode)
        .Or<TaskCanceledException>()
        .WaitAndRetryAsync(
            5,
            sleepDurationProvider: (retries) => { return TimeSpan.FromMilliseconds(1); },
            onRetryAsync: onRetryAsync
        );
};
private只读RetryPolicyDelegate retryPolicyDummy=(
Func sleepDurationProvider,
函数onRetryAsync
) =>
{
返回策略.HandleResult(
r=>!r.IsSuccessStatusCode)
.或()
.WaitAndRetryAsync(
5.
sleepDurationProvider:(重试)=>{return TimeSpan.FromMillicles(1);},
onRetryAsync:onRetryAsync
);
};
这是因为当测试HttpClient.SendAsync方法在所有5次重试后失败时发生的情况时,由于默认值
SleepDuration
返回指数
TimeSpan
,测试可能需要将近两分钟才能完成。但是,当使用上面显示的虚拟重试策略委托时,
SleepDuration
方法从未调用,因此从未测试过

本质上,我想测试
SleepDuration
方法是否执行了它应该执行的操作,返回指数级更高的TimeSpan值,但在测试涉及重试的正常API逻辑时,在执行下一次重试之前根本不等待时间


我能想到的唯一解决方案是将
CreateRetryPolicy
方法与默认的
SleepDuration
RetryAction
方法一起移动到一个helper类中,这样它们就可以公开访问,因此就可以进行单元测试,并保持“无等待”重试策略委托以对单个API进行单元测试。

欢迎使用StackOverflow。你能和我们分享一下你的相关单元测试吗?您使用的是哪种模拟框架?这些可能会极大地影响您处理单元测试的方式。例如,如果您正在使用Moq,那么您可以使用
Moq.Protected
模拟受保护的虚拟/抽象成员。
private readonly RetryPolicyDelegate retryPolicyDummy = (
    Func<int, TimeSpan> sleepDurationProvider,
    Func<DelegateResult<HttpResponseMessage>, TimeSpan, Context, Task> onRetryAsync
) =>
{
    return Policy.HandleResult<HttpResponseMessage>(
            r => !r.IsSuccessStatusCode)
        .Or<TaskCanceledException>()
        .WaitAndRetryAsync(
            5,
            sleepDurationProvider: (retries) => { return TimeSpan.FromMilliseconds(1); },
            onRetryAsync: onRetryAsync
        );
};