0

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.

8
  • 1
    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. Commented Sep 21, 2020 at 15:48
  • 1
    @DanielLorenz so essentially, do I need to give Client a new constructor that receives an HttpMessageHandler, use it in the method to be tested, and then pass _messageHandlerMock to it from the unit test? Commented Sep 21, 2020 at 16:08
  • Excactly. Otherwise, it would be newing up its own inside the Client class. Commented Sep 21, 2020 at 17:35
  • @francachu Have you passed the _clientMock HttpMessageHandler to the httpClient HttpClient at its constructor? Commented Sep 22, 2020 at 6:36
  • @francachu there is something that I don't understand, from the method, you call to client.GetAsync(string url) but you verify SendAsync that get HttpRequestMessage as argument. If you replace ItExpr.Is<HttpRequestMessage>(req => req.Method == HttpMethod.Get && req.RequestUri == new Uri(url)) with ItExpr.Is<HttpRequestMessage>() the test will pass? Commented Sep 22, 2020 at 14:44

1 Answer 1

0

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.

Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.