Home > Back-end >  How to mock AsyncPolicyWrap or AsyncPolicy in .Net Core using FakeItEasy
How to mock AsyncPolicyWrap or AsyncPolicy in .Net Core using FakeItEasy

Time:04-01

I have a code like this (I have to test a repo, you'll see the code below)

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  
    }
}

then I have another 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 pieces of code above can't be changed cause they are in prod and I as a junior dev just have to cover code with tests if possible

I have tried to write a test like this

[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);
}

here is an error i get The error itself

I get similar for commented // var test = ... variats

CodePudding user response:

Concrete vs Abstract

Whenever you need to mock something then rely on abstraction rather than concrete implementation.

AsyncPolicyWrap is a concrete class not an abstract like AsyncPolicy

Also as the exception says this class does not have a public parameterless constructor.
It has an internal ctor with 2 parameters:

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

So, you should prefer AsyncPolicy abstract class or IAsyncPolicy interface.

With or without result

Please be aware that in Polly each Policy has two versions:

  • One which does not return any result
  • One which does return some result

Based on the SomeRepo's code your Policy should return an HttpResponseMessage.

So, you should use IAsyncPolicy<HttpResponseMessage> or AsyncPolicy<HttpResponseMessage> to indicate that your policy will return an HttpResponseMessage.

Mocking

Whenever you mock an IAsyncPolicy<HttpResponseMessage> then you don't have to recreate the combined policy (like you did in the comments). All you have to do is to define how should the ExecuteAsync behave.

Happy path:

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

Unhappy path:

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

I've used moq to mock the policy but the same concept can be applied for FakeItEasy.

  • Related