如何使用 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。
我有这样的代码(我必须测试一个 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。