mock HttpClient SendAsync method



我的代码是使用HttpClient来检索一些数据

HttpClient client = new HttpClient
{
BaseAddress = new Uri("myurl.com"),
};
var msg = new HttpRequestMessage(HttpMethod.Get, "myendpoint");
var res = await client.SendAsync(msg);

我怎么能模拟这个SendAsync方法在HttpClient和注入它内。net核心ServiceCollection?

我试着像这样嘲笑

var mockFactory = new Mock<IHttpClientFactory>();
var mockHttpMessageHandler = new Mock<HttpMessageHandler>();
mockHttpMessageHandler.Protected()
.Setup<Task<HttpResponseMessage>>("SendAsync", ItExpr.IsAny<HttpRequestMessage>(), ItExpr.IsAny<CancellationToken>())
.ReturnsAsync(new HttpResponseMessage
{
StatusCode = HttpStatusCode.OK,
Content = new StringContent("{'name':thecodebuzz,'city':'USA'}"),
});
var client = new HttpClient(mockHttpMessageHandler.Object);
mockFactory.Setup(_ => _.CreateClient(It.IsAny<string>())).Returns(client);

但是如何将这个mockFactory注入ServiceCollection?或者可能有更简单或不同的方法?

与其嘲弄HTTP调用,为什么不封装它呢?然后可以模拟封装/抽象。

例如:

interface IClient
{
Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken = default);
}
class HttpClientAdapter : IClient
{
readonly HttpClient _client;
public HttpClientAdapter(HttpClient client)
{
_client = client;
}
public Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken = default) => _client.SendAsync(request, cancellationToken);
}

让你的代码依赖于IClient接口。在正常使用期间,您将在HttpClientAdapter实现中使用真正的HttpClient。对于测试,您可以模拟IClient

请注意,将抽象设置在比这高一点的级别可能对您更有用。例如,如果你希望从HTTP响应中解析JSON字符串到一些DataObject,那么也许你的IClient接口应该看起来更像这样:

class DataObject
{
public string Name { get; set; }
public string City { get; set; }
}
interface IClient
{
Task<DataObject> GetAsync(CancellationToken cancellationToken = default);
}
public class ClientImplementation : IClient
{
readonly HttpClient _client;
public ClientImplementation(HttpClient client)
{
_client = client;
}
public async Task<DataObject> GetAsync(CancellationToken cancellationToken = default)
{
var response = await _client.SendAsync(...);
var dataObject = new DataObject();
// parse the response into the data object
return dataObject;
}
}

在这里画线的好处是您的测试将有更少的工作要做。例如,您的模拟代码不必设置HttpResponseMessage对象。

选择在哪里为抽象绘制边界完全取决于您。但关键的收获是:一旦你的代码依赖于一个小接口,那么模拟这个接口和测试你的代码就很容易了。

如果你真的需要模拟HttpClient本身,看看这个库:https://github.com/richardszalay/mockhttp

From the docs:

var mockHttp = new MockHttpMessageHandler();
// Setup a respond for the user api (including a wildcard in the URL)
mockHttp.When("http://localhost/api/user/*")
.Respond("application/json", "{'name' : 'Test McGee'}"); // Respond with JSON
// Inject the handler or client into your application code
var client = mockHttp.ToHttpClient();
var response = await client.GetAsync("http://localhost/api/user/1234");
// or without async: var response = client.GetAsync("http://localhost/api/user/1234").Result;
var json = await response.Content.ReadAsStringAsync();
// No network connection required
Console.Write(json); // {'name' : 'Test McGee'}

最新更新