C# Polly AsyncPolicy的单元测试SleepDurationProvider方法 当前解决方案
我有一个C#库,其中包含一个拥有多个API的主类,并以各种方式使用所有API。每个API都将其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
HttpClient.SendAsync
包装在PollyAsyncPolicy.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
);
};