原文地址: 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对象。

  1. public class FakeHttpMessageHandler : DelegatingHandler
  2. {
  3. private HttpResponseMessage _fakeResponse;
  4. public FakeHttpMessageHandler(HttpResponseMessage responseMessage)
  5. {
  6. _fakeResponse = responseMessage;
  7. }
  8. protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
  9. {
  10. return await Task.FromResult(_fakeResponse);
  11. }
  12. }

这里我添加了一个需要HttpResponseMessage构造函数,然后复写了SendAsync方法, 在该方法中直接返回了构造函数中传入的HttpResponseMessage对象。

编写一个使用IHttpClientFactory接口的服务

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

  1. public class UserService
  2. {
  3. private readonly IHttpClientFactory _httpFactory;
  4. public UserService(IHttpClientFactory httpFactory)
  5. {
  6. _httpFactory = httpFactory;
  7. }
  8. public async Task<List<User>> GetUsers(string url)
  9. {
  10. using (HttpClient httpclient = _httpFactory.CreateClient())
  11. {
  12. using (HttpResponseMessage response = await httpclient.GetAsync(url))
  13. {
  14. if (response.StatusCode == HttpStatusCode.OK)
  15. {
  16. List<User> users = await response.Content.ReadAsAsync<List<User>>();
  17. return users;
  18. }
  19. return null;
  20. }
  21. }
  22. }
  23. }

以下是Api请求返回的用户类

  1. public class User
  2. {
  3. public string FirstName { get; set; }
  4. public string LastName { get; set; }
  5. }

如你所见,使用HttpClientFactory允许我们模拟HttpClient实例化

测试服务

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

测试场景1: 模拟一个请求,返回2个用户

  1. public class UserServiceTests
  2. {
  3. [Fact]
  4. public async Task WhenACorrectUrlIsProvided_ServiceShouldReturnAlistOfUsers()
  5. {
  6. // Arrange
  7. var users = new List<User>
  8. {
  9. new User
  10. {
  11. FirstName = "John",
  12. LastName = "Doe"
  13. },
  14. new User
  15. {
  16. FirstName = "John",
  17. LastName = "Deere"
  18. }
  19. };
  20. var httpClientFactoryMock = Substitute.For<IHttpClientFactory>();
  21. var url = "http://good.uri";
  22. var fakeHttpMessageHandler = new FakeHttpMessageHandler(new HttpResponseMessage() {
  23. StatusCode = HttpStatusCode.OK,
  24. Content = new StringContent(JsonConvert.SerializeObject(users), Encoding.UTF8, "application/json")
  25. });
  26. var fakeHttpClient = new HttpClient(fakeHttpMessageHandler);
  27. httpClientFactoryMock.CreateClient().Returns(fakeHttpClient);
  28. // Act
  29. var service = new UserService(httpClientFactoryMock);
  30. var result = await service.GetUsers(url);
  31. // Assert
  32. result
  33. .Should()
  34. .BeOfType<List<User>>()
  35. .And
  36. .HaveCount(2)
  37. .And
  38. .Contain(x => x.FirstName == "John")
  39. .And
  40. .Contain(x => x.LastName == "Deere")
  41. .And
  42. .Contain(x => x.LastName == "Doe");
  43. }
  44. }
  • 在以上测试中,我们期望获取一个成功的响应,并得到2个用户的信息。
  • 我们期望从Service中得到的数据是JSON格式的。
  • 我们使用一个伪造的处理程序初始化了一个HttpClient对象,然后定义了我们期望的得到的伪造对象httpClientFactoryMock.CreateClient().Returns(fakeHttpClient);

测试场景2: 模拟一个404错误,返回空数据

  1. public class UserServiceTests
  2. {
  3. [Fact]
  4. public async Task WhenABadUrlIsProvided_ServiceShouldReturnNull()
  5. {
  6. // Arrange
  7. var httpClientFactoryMock = Substitute.For<IHttpClientFactory>();
  8. var url = "http://bad.uri";
  9. var fakeHttpMessageHandler = new FakeHttpMessageHandler(new HttpResponseMessage() {
  10. StatusCode = HttpStatusCode.NotFound
  11. });
  12. var fakeHttpClient = new HttpClient(fakeHttpMessageHandler);
  13. httpClientFactoryMock.CreateClient().Returns(fakeHttpClient);
  14. // Act
  15. var service = new UserService(httpClientFactoryMock);
  16. var result = await service.GetUsers(url);
  17. // Assert
  18. result
  19. .Should()
  20. .BeNullOrEmpty();
  21. }
  22. }
  • 和测试场景1类似,当一个Http请求返回Not Found, 它的结果集是Null

总结

本篇作者讲解了在ASP.NET Core中如何伪造HttpClient来测试持有HttpClient对象的类。这里主要是通过伪造的DelegatingHandler对象来创建一个HttpClient对象,并使用IHttpClientFactory来获取伪造的HttpClient来达到目的。

本篇源代码:https://github.com/lamondlu/Sample_TestHttpClient

ASP.NET Core中如何针对一个使用HttpClient对象的类编写单元测试的更多相关文章

  1. 如何在ASP.NET Core中实现一个基础的身份认证

    注:本文提到的代码示例下载地址> How to achieve a basic authorization in ASP.NET Core 如何在ASP.NET Core中实现一个基础的身份认证 ...

  2. [转]如何在ASP.NET Core中实现一个基础的身份认证

    本文转自:http://www.cnblogs.com/onecodeonescript/p/6015512.html 注:本文提到的代码示例下载地址> How to achieve a bas ...

  3. ASP.NET Core中如果Response.HasStarted已经为true,就不能更改Response.Cookies和Response.Headers等属性的值了

    最近我在ASP.NET Core中做了一个中间件CustomizedMiddleware,要说该中间件的功能也很简单,其实就是往HttpResponse中添加一个Cookie而已,但是我将添加Cook ...

  4. gRPC在 ASP.NET Core 中应用学习(二)

    前言: 上一篇文章中简单的对gRPC进行了简单了解,并实现了gRPC在ASP.NET Core中服务实现.客户端调用:那么本篇继续对gRPC的4中服务方法定义.其他使用注意点进一步了解学习 一.gRP ...

  5. 3、带你一步一步学习ASP.NET Core中的配置之Configuration

    如果你是刚接触ASP.NET Core的学习的话,你会注意到:在ASP.NET Core项目中,看不到.NET Fraemwork时代中的web.config文件和app.config文件了.那么你肯 ...

  6. 在ASP.NET Core中实现一个Token base的身份认证

    注:本文提到的代码示例下载地址> How to achieve a bearer token authentication and authorization in ASP.NET Core 在 ...

  7. 在ASP.NET Core中通过EF Core实现一个简单的全局过滤查询

    前言 不知道大家是否和我有同样的问题: 一般在数据库的设计阶段,会制定一些默认的规则,其中有一条硬性规定就是一定不要对任何表中的数据执行delete硬删除操作,因为每条数据对我们来说都是有用的,并且是 ...

  8. 从零搭建一个IdentityServer——聊聊Asp.net core中的身份验证与授权

    OpenIDConnect是一个身份验证服务,而Oauth2.0是一个授权框架,在前面几篇文章里通过IdentityServer4实现了基于Oauth2.0的客户端证书(Client_Credenti ...

  9. ASP.NET Core 中的那些认证中间件及一些重要知识点

    前言 在读这篇文章之间,建议先看一下我的 ASP.NET Core 之 Identity 入门系列(一,二,三)奠定一下基础. 有关于 Authentication 的知识太广,所以本篇介绍几个在 A ...

随机推荐

  1. excel导出使用get请求参数过长问题

    遇到的问题: excel导出功能时,使用的是window.location.href=url也就是get请求.当传入参数过长的时候就报了414,地址过长的错误. 解决思路: 将get请求换为post请 ...

  2. That girl

    音标 词汇 Purple Glasses Black Hat Brown Bag Clothes Blue Jacket Pink Handbag Sock White Skirt Shoe 1, s ...

  3. JVM虚拟机个人理解

    针对于java1.8版本,JVM的系统架构 类加载机制: 堆内存结构图: 面试题:一个对象从创建到销毁经历了什么? 1.new一个对象时,在堆内存中开辟一块空间. 2.给开辟的空间分配一个地址. 3. ...

  4. 机器学习——KNN算法(k近邻算法)

    一 KNN算法 1. KNN算法简介 KNN(K-Nearest Neighbor)工作原理:存在一个样本数据集合,也称为训练样本集,并且样本集中每个数据都存在标签,即我们知道样本集中每一数据与所属分 ...

  5. Linux_常用命令简单介绍(netstat,awk,top,tail,head,less,more,cat,nl)

    1.netstat netstat -tnl | grep 443 (查看443端口是否被占用) root用户,用netstat -pnl | grep 443 (还可显示出占用本机443端口的进程P ...

  6. vue事件修饰符

    阻止单击事件冒泡 <a v-on:click.stop="doThis"></a>提交事件不再重载页面<form v-on:submit.preven ...

  7. [LeetCode] Buddy Strings 伙计字符串

    Given two strings A and B of lowercase letters, return true if and only if we can swap two letters i ...

  8. 如何在mysql客户端即mysql提示符下执行操作系统命令

    环境描述: mysql版本:5.5.57-log 操作系统版本:Red Hat Enterprise Linux Server release 6.6 (Santiago) 需求描述: 在mysql的 ...

  9. ivew ui

    render操作: render:(h, params) => { return h('div', [ h('Button',{ on:{ click:()=>{ this.edit(pa ...

  10. jieba库的使用与词频统计

    1.词频统计 (1)词频分析是对文章中重要词汇出现的次数进行统计与分析,是文本 挖掘的重要手段.它是文献计量学中传统的和具有代表性的一种内容分析方法,基本原理是通过词出现频次多少的变化,来确定热点及其 ...