zoukankan      html  css  js  c++  java
  • ASP.NET Core中如何针对一个使用HttpClient对象的类编写单元测试

    原文地址: How to unit test a class that consumes an HttpClient with IHttpClientFactory in ASP.NET Core?
    作者: Anthony Giretti
    译者: Lamond Lu

    介绍

    几年前,微软引入了HttpClient类来替代HttpWebRequest来发送Web请求。这个新的类更易于使用,更加简洁,更具有异步性,且易于扩展。

    HttpClient类有一个可以接受HttpMessageHandler类对象的构造函数。HttpMessageHandler类对象可以接受一个请求(HttpRequestMessage), 并返回响应(HttpResponseMessage)。它的功能完全取决于它的实现。默认情况下HttpClient使用的是HttpClientHandlerHttpClientHandler是一个处理程序,它向网络服务器发送请求并从服务器返回响应。在本篇博文中,我们将通过继承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实例化

    测试服务

    在下面的单元测试中,我们会使用XUnitFluentAssertionNSubstitute

    测试场景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

  • 相关阅读:
    数据库设计步骤 java程序员
    (1) 语法结构 java程序员
    (3)JavaScript学习笔记 函数、对象、数组 java程序员
    JavaScript 学习计划 java程序员
    (5)JavaScript学习笔记 变量 java程序员
    (2) 数据类型、值 及 字符串 java程序员
    (4)JavaScript学习笔记 数据类型和值(续) java程序员
    J2EE、J2SE、J2ME是什么意思? java程序员
    80端口(该端口是Tomcat的监听端口)已经被其他程序占用 java程序员
    (17) string 和 stringbuilder java程序员
  • 原文地址:https://www.cnblogs.com/lwqlun/p/10215929.html
Copyright © 2011-2022 走看看