ASP.NET Core中如何針對一個使用HttpClient對象的類編寫單元測試

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

介紹

幾年前,微軟引入了HttpClient類來替代HttpWebRequest來發送Web請求。這個新的類更易於使用,更加簡潔,更具備異步性,且易於擴展。github

HttpClient類有一個能夠接受HttpMessageHandler類對象的構造函數。HttpMessageHandler類對象能夠接受一個請求(HttpRequestMessage), 並返回響應(HttpResponseMessage)。它的功能徹底取決於它的實現。默認狀況下HttpClient使用的是HttpClientHandlerHttpClientHandler是一個處理程序,它向網絡服務器發送請求並從服務器返回響應。在本篇博文中,咱們將經過繼承DelegatingHandler來建立本身的HttpMessageHandlerjson

爲了實現以上功能,HttpClient對象不能夠直接使用,而是須要與容許使用IHttpClientFactory接口進行模擬的依賴注入一塊兒使用。c#

讓咱們來僞造一個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對象。app

編寫一個使用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請求返回的用戶類async

public class User
{
   public string FirstName { get; set; }
   public string LastName { get; set; }
}

如你所見,使用HttpClientFactory容許咱們模擬HttpClient實例化ide

測試服務

在下面的單元測試中,咱們會使用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

相關文章
相關標籤/搜索