咔叽游戏

 找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 203|回复: 0

[ASP.NET] ASP.NET Core针对一个使用HttpClient对象的类编写单元测试详解

[复制链接]
  • TA的每日心情
    无聊
    2019-5-27 08:20
  • 签到天数: 4 天

    [LV.2]圆转纯熟

    发表于 2020-2-28 10:19:59 | 显示全部楼层 |阅读模式
    介绍

    几年前,微软引入了HttpClient类来替代HttpWebRequest来发送Web请求。这个新的类更易于使用,更加简洁,更具有异步性,且易于扩展。
    HttpClient类有一个可以接受HttpMessageHandler类对象的构造函数。HttpMessageHandler类对象可以接受一个请求(HttpRequestMessage), 并返回响应(HttpResponseMessage)。它的功能完全取决于它的实现。默认情况下HttpClient使用的是HttpClientHandler,HttpClientHandler是一个处理程序,它向网络服务器发送请求并从服务器返回响应。在本篇博文中,我们将通过继承DelegatingHandler来创建自己的HttpMessageHandler。
    为了实现以上功能,HttpClient对象不可以直接使用,而是需要与允许使用IHttpClientFactory接口进行模拟的依赖注入一起使用。
    让我们来伪造一个HttpMessageHandler

    下面的例子中,我们只讨论HttpResponseMessage, 不会处理HttpRequestMessage。
    以下是我伪造的一个HttpMessageHandler对象。

    public class FakeHttpMessageHandler : DelegatingHandler
    {
    private HttpResponseMessage _fakeResponse;

    public FakeHttpMessageHandler(HttpResponseMessage responseMessage)
    {
      _fakeResponse = responseMessage;
    }

    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
      return await Task.FromResult(_fakeResponse);
    }
    }这里我添加了一个需要HttpResponseMessage构造函数,然后复写了SendAsync方法, 在该方法中直接返回了构造函数中传入的HttpResponseMessage对象。
    编写一个使用IHttpClientFactory接口的服务

    下面我们需要编写一个UserService类,这个类提供了一个GetUsers方法,来从远程服务器端获取用户列表。

    public class UserService
    {
    private readonly IHttpClientFactory _httpFactory;

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

    public async Task<List<User>> GetUsers(string url)
    {
      using (HttpClient httpclient = _httpFactory.CreateClient())
      {
       using (HttpResponseMessage response = await httpclient.GetAsync(url))
       {
        if (response.StatusCode == HttpStatusCode.OK)
        {
         List<User> users = await response.Content.ReadAsAsync<List<User>>();
         return users;
        }
        return null;
       }
      }
    }
    }以下是Api请求返回的用户类

    public class User
    {
    public string FirstName { get; set; }
    public string LastName { get; set; }
    }如你所见,使用HttpClientFactory允许我们模拟HttpClient实例化
    测试服务

    在下面的单元测试中,我们会使用XUnit、FluentAssertion、NSubstitute
    测试场景1: 模拟一个请求,返回2个用户


    public class UserServiceTests
    {
      [Fact]
      public async Task WhenACorrectUrlIsProvided_ServiceShouldReturnAlistOfUsers()
      {
        // Arrange
        var users = new List<User>
        {
         new User
         {
           FirstName = "John",
           LastName = "Doe"
         },
         new User
         {
           FirstName = "John",
           LastName = "Deere"
         }
        };

        var httpClientFactoryMock = Substitute.For<IHttpClientFactory>();
        var url = "http://good.uri";
        var fakeHttpMessageHandler = new FakeHttpMessageHandler(new HttpResponseMessage() {
         StatusCode = HttpStatusCode.OK,
         Content = new StringContent(JsonConvert.SerializeObject(users), Encoding.UTF8, "application/json")
        });
        var fakeHttpClient = new HttpClient(fakeHttpMessageHandler);

        httpClientFactoryMock.CreateClient().Returns(fakeHttpClient);

        // Act
        var service = new UserService(httpClientFactoryMock);
        var result = await service.GetUsers(url);

       // Assert
       result
       .Should()
       .BeOfType<List<User>>()
       .And
       .HaveCount(2)
       .And
       .Contain(x => x.FirstName == "John")
       .And
       .Contain(x => x.LastName == "Deere")
       .And
       .Contain(x => x.LastName == "Doe");
      }
    }
      在以上测试中,我们期望获取一个成功的响应,并得到2个用户的信息。我们期望从Service中得到的数据是JSON格式的。我们使用一个伪造的处理程序初始化了一个HttpClient对象,然后定义了我们期望的得到的伪造对象httpClientFactoryMock.CreateClient().Returns(fakeHttpClient);
    测试场景2: 模拟一个404错误,返回空数据


    public class UserServiceTests
    {
      [Fact]
      public async Task WhenABadUrlIsProvided_ServiceShouldReturnNull()
      {
        // Arrange
        var httpClientFactoryMock = Substitute.For<IHttpClientFactory>();
        var url = "http://bad.uri";
        var fakeHttpMessageHandler = new FakeHttpMessageHandler(new HttpResponseMessage() {
         StatusCode = HttpStatusCode.NotFound
        });
        var fakeHttpClient = new HttpClient(fakeHttpMessageHandler);

        httpClientFactoryMock.CreateClient().Returns(fakeHttpClient);

        // Act
        var service = new UserService(httpClientFactoryMock);
        var result = await service.GetUsers(url);

       // Assert
       result
       .Should()
       .BeNullOrEmpty();
      }
    }和测试场景1类似,当一个Http请求返回Not Found, 它的结果集是Null

    总结

    本篇作者讲解了在ASP.NET Core中如何伪造HttpClient来测试持有HttpClient对象的类。这里主要是通过伪造的DelegatingHandler对象来创建一个HttpClient对象,并使用IHttpClientFactory来获取伪造的HttpClient来达到目的。
    本篇源代码:https://github.com/lamondlu/Sample_TestHttpClient (本地下载)
    好了,以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对咔叽网单www.2nzz.com的支持。
    原文地址: How to unit test a class that consumes an HttpClient with IHttpClientFactory in ASP.NET Core?

    作者: Anthony Giretti

    QQ|免责声明|小黑屋|手机版|Archiver|咔叽游戏

    GMT+8, 2024-3-28 23:24

    Powered by Discuz! X3.4

    © 2001-2023 Discuz! Team.

    快速回复 返回顶部 返回列表