简体   繁体   中英

How to mock GetAsync when it is used from a class that extends HttpClient?

I've read a bit about mocking HttpClient, and have found several questions such as this one or this one which all state that when we want to mock HttpClient methods such as GetAsync, we must instead mock SendAsync which is called by all HttpClient methods, like so:

Mock<HttpMessageHandler> _clientMock = new Mock<HttpMessageHandler>(MockBehavior.Strict);

_clientMock.Protected().Setup<Task<HttpResponseMessage>>("SendAsync", ItExpr.IsAny<HttpRequestMessage>(), ItExpr.IsAny<CancellationToken>())
    .Returns(Task.FromResult(someResponse)).Verifiable();

var x = httpClient.GetAsync("someURL");

_clientMock.Protected().Verify(
    "SendAsync",
    Times.Exactly(1),
    ItExpr.Is<HttpRequestMessage>(req => req.Method == HttpMethod.Get && req.RequestUri == new Uri("someURL")),
    ItExpr.IsAny<CancellationToken>()
);

This works fine, and if I run the test exactly like this, it obviously passes.

However, the method I'm testing, which looks like this:

public async Task<Response<IEnumerable<Instance>>> GetInstancesAsync(string accessToken, bool? enable = null)
{
    try
    {
        using (var client = new Client(this._baseUrl, accessToken))
        {
            var url = string.Format("configurations/instances?enable={0}", enable);

            // Creates a request and returns the result
            HttpResponseMessage response = await client.GetAsync(url).ConfigureAwait(false);
            return await response.Content.ReadAsAsync<Response<IEnumerable<Instance>>>().ConfigureAwait(false);
        }
    }
    catch { }
    return null;
}

As you can see, it uses a Client, which is a class we have created and that extends HttpClient, and I think the problem could be here. When I try to test this method, the solution mentioned above isn't working, and SendAsync is never being called. I don't understand why.

My unit test currently looks like this:

_messageHandlerMock.Protected().Setup<Task<HttpResponseMessage>>("SendAsync", ItExpr.IsAny<HttpRequestMessage>(), ItExpr.IsAny<CancellationToken>())
    .Returns(Task.FromResult(responseMessage)).Verifiable();

var result = await _configurationsProxy.GetInstancesAsync(ACCESS_TOKEN, true);

Assert.IsNotNull(result);
_messageHandlerMock.Protected().Verify(
    "SendAsync",
    Times.Exactly(1),
    ItExpr.Is<HttpRequestMessage>(req => req.Method == HttpMethod.Get && req.RequestUri == new Uri(url)),
    ItExpr.IsAny<CancellationToken>()
);

The line _messageHandlerMock.Protected().Verify(...) is failing, resulting in

Moq.MockException: Expected invocation on the mock exactly 1 times, but was 0 times", and Assert.IsNotNull(result) also fails, with result being null.

I'll post the official answer here, then. Glad it worked!

You aren't using the _messageHandlerMock in your new Client(...) anywhere, so its using its own internal version instead of the mock you created. Thus, it bypasses your mock check. It ends up newing up its own instance inside the Client class.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM