如何使用 FakeItEasy 在 .Net Core 中模拟 AsyncPolicyWrap 或 AsyncPolicy

How to mock AsyncPolicyWrap or AsyncPolicy in .Net Core using FakeItEasy

我有这样的代码(我必须测试一个 repo,你会看到下面的代码)

public class SomeClass
{
    public AsyncPolicyWrap PropName { get; }
    
    public SomeClass(...)
    {     
        PropName = Policy.WrapAsync(someRetry,someCircuitBreaker)
        // here there are passed some methods that return someRetry - AsyncRetryPolicy  
        // and  someCircuitBreaker - AsyncCircuitBreakerPolicy  
    }
}

然后我有另一个 repo class

public class SomeRepo : ISomeRepo
{
    private readonly AsyncPolicy _somePolicy;
    
    public SomeRepo(..., SomeClass someClass) : base(...)
    {
        _somePolicy = someClass.PropName;
    }
    public async Task<Result<SomeDTO>> GetDTO(Guid someId)
    {
        var someResponse = await _somePolicy.ExecuteAsync(() =>
            HttpClient.GetAsync(serviceName, $"endpointUrl"));
        ...
    }
}

上面的 2 段代码无法更改,因为它们正在生产中,作为初级开发人员,我只需要尽可能地用测试覆盖代码

我试过写这样的测试

[TestMethod] 
public async Task DoStuff()
{
    var repository = DefaultSome();

    var result = await repository.GetDTO(new Guid());

    result.ShouldNotBeNull(); // don't pay attention I'll change stuff which has to be asserted
}
private SomeRepo DefaultSome(Some some = null) 
{
    some = some ?? A.Fake<ISome>();
    /// HERE I TRIED TO MOCK STUFF IN DIFFERENT WAYS AND I HAVE AN ERROR
    var policyWrap = A.Dummy<AsyncPolicyWrap>();
    //var test = Policy.WrapAsync(A.Fake<AsyncRetryPolicy>(), A.Fake<AsyncCircuitBreakerPolicy>());
    //var test = Policy.WrapAsync(A.Fake<IAsyncPolicy>(), A.Fake<IAsyncPolicy>());

    A.CallTo(() =>
            policyWrap.ExecuteAsync(A<Func<Task<HttpResponseMessage>>>._))
        .Returns(new HttpResponseMessage(HttpStatusCode.OK));

    var policy = A.Fake<RetryPolicies>();
    A.CallTo(() =>
            policy.PropName)
        .Returns(policyWrap);

    return new SomeRepo(some, ..., policy);
}

这是我得到的一个错误

我得到类似的评论 // var test = ... variats

具体与抽象

每当你需要模拟某些东西时,就依赖于抽象而不是具体的实现。

AsyncPolicyWrap is a concrete class not an abstract like AsyncPolicy

也如异常所述,此 class 没有 public 无参数构造函数。
它有一个带有 2 个参数的 internal 构造函数:

internal AsyncPolicyWrap(AsyncPolicy outer, IAsyncPolicy inner)
    : base(outer.ExceptionPredicates)
{
    _outer = outer;
    _inner = inner;
}

因此,您应该更喜欢 AsyncPolicy abstract class 或 IAsyncPolicy 界面。

有或没有结果

请注意,在 Polly 中,每个策略都有两个版本:

  • 没有return任何结果
  • return 一些结果

根据 SomeRepo 的代码,您的政策应该 return 一个 HttpResponseMessage

因此,您应该使用 IAsyncPolicy<HttpResponseMessage>AsyncPolicy<HttpResponseMessage> 来表明您的政策将 return 和 HttpResponseMessage

嘲讽

每当你模拟 IAsyncPolicy<HttpResponseMessage> 时,你就不必重新创建组合策略(就像你在评论中所做的那样)。您所要做的就是定义 ExecuteAsync 的行为方式。

幸福之路:

var mockedPolicy = new Mock<IAsyncPolicy<HttpResponseMessage>>();
mockedPolicy
    .Setup(policy => policy.ExecuteAsync(It.IsAny<Func<Task<HttpResponseMessage>>>()))
    .ReturnsAsync(new HttpResponseMessage(HttpStatusCode.OK));

不幸的道路:

var mockedPolicy = new Mock<IAsyncPolicy<HttpResponseMessage>>();
mockedPolicy
    .Setup(policy => policy.ExecuteAsync(It.IsAny<Func<Task<HttpResponseMessage>>>()))
    .ThrowsAsync(new HttpRequestException("Something bad happened"));

我已经使用最小起订量来模拟政策,但同样的概念也适用于 FakeItEasy。