首页 > 解决方案 > 从扩展 HttpClient 的类中使用 GetAsync 时如何模拟它?

问题描述

我已经阅读了一些关于模拟 HttpClient 的内容,并且发现了几个问题,例如this onethis one,它们都指出,当我们想要模拟 GetAsync 等 HttpClient 方法时,我们必须改为模拟所有 HttpClient 方法调用的 SendAsync,像这样:

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

如您所见,它使用了一个 Client,这是我们创建的一个类,它扩展了 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 :对模拟的预期调用恰好 1 次,但为 0 次”,并且 Assert.IsNotNull(result) 也失败,结果为空。

标签: c#unit-testingnunitmoqdotnet-httpclient

解决方案


那我把官方答案贴在这里。很高兴它奏效了!

您没有在任何地方的新 Client(...) 中使用 _messageHandlerMock,因此它使用自己的内部版本而不是您创建的模拟。因此,它绕过了您的模拟检查。它最终在 Client 类中更新了自己的实例。


推荐阅读