首页 > 解决方案 > 在 Xunit 测试中使用 Moq 库模拟 HttpClient GetAsync

问题描述

我正在为这个简单地调用外部 API 的小服务编写一个简单的单元测试:

public class ApiCaller : IApiCaller
{
    private readonly IHttpClientFactory _httpFactory;

    public ApiCaller(IHttpClientFactory httpFactory)
    {
        _httpFactory = httpFactory;
    }

    public async Task<T> GetResponseAsync<T>(Uri url)
    {
        using (HttpClient client = _httpFactory.CreateClient())
        {
            client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
            client.Timeout = TimeSpan.FromSeconds(20);
            using (HttpResponseMessage response = await client.GetAsync(url))
            {
                response.EnsureSuccessStatusCode();
                string responseBody = await response.Content.ReadAsStringAsync();

                return JsonConvert.DeserializeObject<T>(responseBody);
            }

        }
    }
}

我的第一个问题是:模拟并因此测试此类服务似乎不是很常见的做法,我想知道是否有一些具体的解释。

其次,我尝试编写一个简单的单元测试,但我无法模拟GetAsync调用,因为 HttpClient 没有实现任何接口。

public class ApiCallerTest
{
    private readonly ApiCaller _target;
    private readonly Mock<IHttpClientFactory> _httpClientFactory;


    public ApiCallerTest()
    {
        _httpClientFactory = new Mock<IHttpClientFactory>();
        _target = new ApiCaller(_httpClientFactory.Object);
    }

    [Fact]
    public void WhenACorrectUrlIsProvided_ServiceShouldReturn()
    {


        var client = new HttpClient();
        _httpClientFactory.Setup(x => x.CreateClient(It.IsAny<string>())).Returns(client);

        var httpMessageHandler = new Mock<HttpMessageHandler>();

    }

}

标签: c#unit-testingasp.net-coremoqxunit

解决方案


无论您使用的 HttpClient 类中的方法是什么(GetAsync、PostAsync 等),下面的代码都是您应该使用的。所有这些方法都是为了方便程序员而创建的。他们所做的是使用 HttpMessageHandler 类的SendAsync方法。

var mockHttpMessageHandler = new Mock<HttpMessageHandler>();

// Setup Protected method on HttpMessageHandler mock.
mockHttpMessageHandler.Protected()
    .Setup<Task<HttpResponseMessage>>(
        "SendAsync",
        ItExpr.IsAny<HttpRequestMessage>(),
        ItExpr.IsAny<CancellationToken>()
    )
    .ReturnsAsync((HttpRequestMessage request, CancellationToken token) =>
    {
        HttpResponseMessage response = new HttpResponseMessage();

        // configure your response here

        return response;
    });

然后你以这种方式使用它:

var httpClient = new HttpClient(mockHttpMessageHandler.Object);
var result = await httpClient.GetAsync(url, cancellationToken);

您也可以在这里查看如何为 httpclient getasync 方法创建模拟?


推荐阅读