bad*_*key 4 c# serialization unit-testing moq dotnet-httpclient
嗨,我正在尝试模拟 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 );
...
...
Run Code Online (Sandbox Code Playgroud)
我嘲笑这可能:
...
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();
Run Code Online (Sandbox Code Playgroud)
这会给我一个响应,我可以反序列化和使用它 - 给它一个包含 3 个成员的对象:id、name 和 street。
这对我来说是一个问题 - 我也在同一个单元测试中进行了另一个 httpclient 调用,但我不知道如何模拟它。事情是这样的:
...
...
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 );
...
...
Run Code Online (Sandbox Code Playgroud)
最初 - 如果这是唯一的 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();
...
...
Run Code Online (Sandbox Code Playgroud)
但显然我不能......问题是,我从来没有尝试过这种情况,我有多个 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);
}
}
Run Code Online (Sandbox Code Playgroud)
上面的处理程序仅使用传入请求的 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
Run Code Online (Sandbox Code Playgroud)
现在,在验证被测对象的预期行为时,可以根据需要调用具有自定义处理程序的客户端。