从扩展 HttpClient 的 class 使用时如何模拟 GetAsync?

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

我已经阅读了一些关于模拟 HttpClient 的内容,并发现了几个问题,例如 this one or ,这些问题都说明当我们想要模拟 HttpClient 方法(例如 GetAsync)时,我们必须改为模拟 SendAsync,它被称为通过所有 HttpClient 方法,像这样:

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>()
);

这很好用,如果我 运行 完全像这样测试,它显然通过了。

但是,我正在测试的方法如下所示:

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

如您所见,它使用了一个客户端,这是我们创建的 class 并且扩展了 HttpClient,我认为问题可能出在这里。当我尝试测试此方法时,上面提到的解决方案不起作用,并且永远不会调用 SendAsync。我不明白为什么。

我的单元测试目前是这样的:

_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>()
);

_messageHandlerMock.Protected().Verify(...) 失败,导致

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

那我就post官方回答吧。很高兴它成功了!

你没有在你的 new Client(...) 中的任何地方使用 _messageHandlerMock,所以它使用它自己的内部版本而不是你创建的模拟。因此,它绕过了您的模拟检查。它最终在客户端 class.

中更新了自己的实例