从扩展 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.
中更新了自己的实例
我已经阅读了一些关于模拟 HttpClient 的内容,并发现了几个问题,例如 this one or
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.
中更新了自己的实例