在单个单元测试中多次模拟 httpclient
Mocking httpclient multiple times in a single Unit Test
您好,我正在尝试模拟 httpclient,显然是强制它给我需要的结果。 问题 是,在我的单元测试中,我必须模拟 httpClient 两次,每次都有不同的响应。
...
...
var httpReq = new HttpRequestMessage(
HttpMethod.Get,
$"{config["MY_TEST_ENDPOINT"]/site1}");
var myContent = await response.Content.ReadAsStringAsync();
var myFirstModel = JsonConvert.DeserializeObject<MyFirstModel>(myContent );
...
...
我嘲笑这个可能:
...
myMockedHttp
.Protected()
.Setup<Task<HttpResponseMessage>>(
"SendAsync",
ItExpr.IsAny<HttpRequestMessage>(),
ItExpr.IsAny<CancellationToken>())
.ReturnsAsync(new HttpResponseMessage()
{
StatusCode = HttpStatusCode.OK,
Content = new StringContent("[{'id':'0001'," +
"'name':'Jonah'," +
"'street':'Cambridge Street 1234'}]"
)
})
.Verifiable();
这会给我一个响应,我可以反序列化并使用它 - 给它一个包含 3 个成员的对象:id、name 和 street。
我的问题来了 - 我也在同一个单元测试中有另一个 httpclient-call,但我不知道如何模拟它。它是这样的:
...
...
var httpReq = new HttpRequestMessage(
HttpMethod.Get,
$"{config["MY_TEST_ENDPOINT"]/site2}");
var httpResponseMsg = await _httpClient.SendAsync(httpReq);
var myContent = await response.Content.ReadAsStringAsync();
var myFirstModel = JsonConvert.DeserializeObject<MySecondModel>(myContent );
...
...
最初 - 如果它是唯一的 httpclient 调用,我会像这样模拟它:
...
myMockedHttp
.Protected()
.Setup<Task<HttpResponseMessage>>(
"SendAsync",
ItExpr.IsAny<HttpRequestMessage>(),
ItExpr.IsAny<CancellationToken>())
.ReturnsAsync(new HttpResponseMessage()
{
StatusCode = HttpStatusCode.OK,
Content = new StringContent("[{'id':'34'," +
"'insurance':'Etx'," +
"'insider':'daily'," +
"'collectives':'4'}]"
)
})
.Verifiable();
...
...
但显然我不能...问题是,我从未尝试过需要模拟多个 httpclient 调用的情况。如您所见,它们响应不同的内容,因为它们是不同的对象,需要以这种方式处理。
有人知道如何解决这个问题吗?
有时设置模拟可能并不总是最好的方法。由于 HttpClient
确实依赖于 HttpMessageHandler
,您可以创建一个简单的来处理预期的请求
class TestMessageHandler : HttpMessageHandler {
private readonly IDictionary<string, HttpResponseMessage> messages;
public TestMessageHandler(IDictionary<string, HttpResponseMessage> messages) {
this.messages = messages;
}
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) {
var response = new HttpResponseMessage(HttpStatusCode.NotFound);
if (messages.ContainsKey(request.RequestUri.ToString()))
response = messages[request.RequestUri.ToString()] ?? new HttpResponseMessage(HttpStatusCode.NoContent);
response.RequestMessage = request;
return Task.FromResult(response);
}
}
上面的处理程序只是使用传入请求的 URL 在字典中查找响应。如果需要,它可以重构以处理更复杂的请求,但对于简单的请求,这将很好地服务。
将处理程序传递给为测试创建的客户端
public async Task MyTestMethod() {
//Arrange
Dictionary<string, HttpResponseMessage> messages = new Dictionary<string, HttpResponseMessage>();
messages.Add("https://somesite1.com/ping", new HttpResponseMessage() {
StatusCode = HttpStatusCode.OK,
Content = new StringContent("[{'id':'0001'," +
"'name':'Jonah'," +
"'street':'Cambridge Street 1234'}]"
)
});
messages.Add("https://somesite2.com/ping", new HttpResponseMessage() {
StatusCode = HttpStatusCode.OK,
Content = new StringContent("[{'id':'34'," +
"'insurance':'Etx'," +
"'insider':'daily'," +
"'collectives':'4'}]"
)
});
var client = new HttpClient(new TestMessageHandler(messages));
//...inject client as needed
在验证被测对象的预期行为时,现在可以根据需要调用带有自定义处理程序的客户端。
您好,我正在尝试模拟 httpclient,显然是强制它给我需要的结果。 问题 是,在我的单元测试中,我必须模拟 httpClient 两次,每次都有不同的响应。
...
...
var httpReq = new HttpRequestMessage(
HttpMethod.Get,
$"{config["MY_TEST_ENDPOINT"]/site1}");
var myContent = await response.Content.ReadAsStringAsync();
var myFirstModel = JsonConvert.DeserializeObject<MyFirstModel>(myContent );
...
...
我嘲笑这个可能:
...
myMockedHttp
.Protected()
.Setup<Task<HttpResponseMessage>>(
"SendAsync",
ItExpr.IsAny<HttpRequestMessage>(),
ItExpr.IsAny<CancellationToken>())
.ReturnsAsync(new HttpResponseMessage()
{
StatusCode = HttpStatusCode.OK,
Content = new StringContent("[{'id':'0001'," +
"'name':'Jonah'," +
"'street':'Cambridge Street 1234'}]"
)
})
.Verifiable();
这会给我一个响应,我可以反序列化并使用它 - 给它一个包含 3 个成员的对象:id、name 和 street。
我的问题来了 - 我也在同一个单元测试中有另一个 httpclient-call,但我不知道如何模拟它。它是这样的:
...
...
var httpReq = new HttpRequestMessage(
HttpMethod.Get,
$"{config["MY_TEST_ENDPOINT"]/site2}");
var httpResponseMsg = await _httpClient.SendAsync(httpReq);
var myContent = await response.Content.ReadAsStringAsync();
var myFirstModel = JsonConvert.DeserializeObject<MySecondModel>(myContent );
...
...
最初 - 如果它是唯一的 httpclient 调用,我会像这样模拟它:
...
myMockedHttp
.Protected()
.Setup<Task<HttpResponseMessage>>(
"SendAsync",
ItExpr.IsAny<HttpRequestMessage>(),
ItExpr.IsAny<CancellationToken>())
.ReturnsAsync(new HttpResponseMessage()
{
StatusCode = HttpStatusCode.OK,
Content = new StringContent("[{'id':'34'," +
"'insurance':'Etx'," +
"'insider':'daily'," +
"'collectives':'4'}]"
)
})
.Verifiable();
...
...
但显然我不能...问题是,我从未尝试过需要模拟多个 httpclient 调用的情况。如您所见,它们响应不同的内容,因为它们是不同的对象,需要以这种方式处理。
有人知道如何解决这个问题吗?
有时设置模拟可能并不总是最好的方法。由于 HttpClient
确实依赖于 HttpMessageHandler
,您可以创建一个简单的来处理预期的请求
class TestMessageHandler : HttpMessageHandler {
private readonly IDictionary<string, HttpResponseMessage> messages;
public TestMessageHandler(IDictionary<string, HttpResponseMessage> messages) {
this.messages = messages;
}
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) {
var response = new HttpResponseMessage(HttpStatusCode.NotFound);
if (messages.ContainsKey(request.RequestUri.ToString()))
response = messages[request.RequestUri.ToString()] ?? new HttpResponseMessage(HttpStatusCode.NoContent);
response.RequestMessage = request;
return Task.FromResult(response);
}
}
上面的处理程序只是使用传入请求的 URL 在字典中查找响应。如果需要,它可以重构以处理更复杂的请求,但对于简单的请求,这将很好地服务。
将处理程序传递给为测试创建的客户端
public async Task MyTestMethod() {
//Arrange
Dictionary<string, HttpResponseMessage> messages = new Dictionary<string, HttpResponseMessage>();
messages.Add("https://somesite1.com/ping", new HttpResponseMessage() {
StatusCode = HttpStatusCode.OK,
Content = new StringContent("[{'id':'0001'," +
"'name':'Jonah'," +
"'street':'Cambridge Street 1234'}]"
)
});
messages.Add("https://somesite2.com/ping", new HttpResponseMessage() {
StatusCode = HttpStatusCode.OK,
Content = new StringContent("[{'id':'34'," +
"'insurance':'Etx'," +
"'insider':'daily'," +
"'collectives':'4'}]"
)
});
var client = new HttpClient(new TestMessageHandler(messages));
//...inject client as needed
在验证被测对象的预期行为时,现在可以根据需要调用带有自定义处理程序的客户端。