单元测试HTTP客户端

时间:2020-01-31 14:10:53

标签: c# unit-testing http

我使用HttpClient获得以下代码。我是C#的新手,想学习如何对HttpClient进行单元测试,但不确定从哪里开始。这是我的代码:

 protected override async Task PopulateData()
        {
            using (var client = new HttpClient())
            {
                var token = "private token";
                var requestUrl = api_url_here;
                var authenticatedRequestUrl = requestUrl + $"{token}";
                var response = await client.GetAsync(authenticatedRequestUrl);
                var stringResult = await response.Content.ReadAsStringAsync();

                // do something
            }
        }

我看过很多不同的文章,它们提出了不同的单元测试方法,但是我不确定如何正确使用它们。例如,我在许多网站上都看到过这种单元测试模式:

  [Test]
        public async Task MockingHTTP()
        {
            var requestUri = new Uri("");
            var expectedResponse = "Response";
            var mockHandler = new Mock<HttpMessageHandler>();

            mockHandler.Protected()
                .Setup<Task<HttpResponseMessage>>("SendAsync", ItExpr.IsAny<HttpRequestMessage>(),
                ItExpr.IsAny<CancellationToken>())
                .ReturnsAsync(new HttpResponseMessage(Net.HttpStatusCode.OK));

            var httpClient = new HttpClient(mockHandler.Object);
            var result = await httpClient.GetStringAsync(requestUri).ConfigureAwait(false);
            Assert.AreEqual(expectedResponse, result);

            }
        }

但是我不知道如何将这种方法应用于我的代码。请有人指出我的正确方向,以便成功地对HttpClient进行单元测试?

1 个答案:

答案 0 :(得分:2)

单元测试时要做的第一件事是识别“被测系统”或SUT。那就是您需要验证其行为的东西。 SUT的所有依赖项都应被模拟,不应使用真实版本。

在这种情况下,您使用的是HttpClient,但是您无法在方法中使用其他处理程序。使用不同的处理程序是为HttpClient伪造响应的最简单方法。 HttpClient通过构造函数接受其他处理程序。因此,您需要像这样调整代码:

public class YourClassName
{
    private readonly HttpMessageHandler _httpMessageHandler;

    public YourClassName(HttpMessageHandler httpMessageHandler)
    {
        _httpMessageHandler = httpMessageHandler;
    }

    protected override async Task PopulateData()
    {
        using (var client = new HttpClient(_httpMessageHandler))
        {
            var token = "private token";
            var requestUrl = api_url_here;
            var authenticatedRequestUrl = requestUrl + $"{token}";
            var response = await client.GetAsync(authenticatedRequestUrl);
            var stringResult = await response.Content.ReadAsStringAsync();

            // do something
        }
    }
}

现在它是可单元测试的,因为您可以模拟HttpMessageHandler。但是执行此操作的代码是cumbersome。考虑到使用HttpClient的其他缺陷,最好一开始甚至不要使用HttpClient。参见:

我的团队所做的是改用Flurl。它具有更好的语法,不具有HttpClient具有的IDisposable的怪异特性,并且易于进行单元测试。

protected override async Task PopulateData()
{
    var token = "private token";
    var requestUrl = api_url_here;
    var authenticatedRequestUrl = requestUrl + $"{token}";
    var stringResult = authenticatedRequestUrl.
    var response = await authenticatedRequestUrl.GetAsync();
    var stringResult = await response.Content.ReadAsStringAsync();

    // do something
}

然后您的单元测试变得更加简单:

[Test]
public async Task PopulateDataTest()
{
    var requestUri = new Uri("SomeUriWithAToken");
    var expectedResponse = "some response body";
    var systemUnderTest = new YourClassName();

    using (var httpTest = new HttpTest())
    {
        httpTest.RespondWith(expectedResponse);
        var result = await systemUnderTest.PopulateData();
        Assert.AreEqual(expectedResponse, result);
        httpTest.ShouldHaveCalled(requestUri);
    }
}

Flurl的文档很棒。您可以让它返回各种响应,甚至可以自动反序列化对C#类的响应。