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

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. 关于二进制枚举-计蒜客-得到整数X

    某君有 n个互不相同的正整数,现在他要从这 n 个正整数之中无重复地选取任意个数,并仅通过加法凑出整数 X.求某君有多少种不同的方案来凑出整数 X. 输入格式 第一行,输入两个整数 n,X(1≤n≤2 ...

  2. 其他信息: 未能加载文件或程序集“file:///C:\Program Files (x86)\SAP BusinessObjects\Crystal Reports for .NET Framework 4.0\Common\SAP BusinessObjects Enterprise XI 4.0\win32_x86\dotnet1\crdb_adoplus.dll”或它的某一个依赖

    今天在使用水晶报表的过程中,遇到了这个问题,下面是代码 FormReportView form = new FormReportView(); ReportDocument rptc = new Re ...

  3. BZOJ.2054.疯狂的馒头(并查集)

    BZOJ 倒序处理,就是并查集傻题了.. 并查集就是确定下一个未染色位置的,直接跳到那个位置染.然而我越想越麻烦=-= 以为有线性的做法,发现还是要并查集.. 数据随机线段树也能过去. //18400 ...

  4. Day3----《Pattern Recognition and Machine Learning》Christopher M. Bishop

    其实今天只花了一点点时间来学习这本书, 如果模型的参数过多,而训练数据又不足够多的话,就会出现overfitting. overfitting可以通过regularization来解决,贝叶斯方法也可 ...

  5. 181102 Python环境搭建(安装Sublime Text3)

    利用Pycharm来编写.执行python代码是一个不错的选择,Pycharm的安装的确也很方便.但是偶然看到别人用Sublime Text来编写.执行代码,觉得很酷.所以自己动手搭建环境. 1. 下 ...

  6. vue 源码学习二 实例初始化和挂载过程

    vue 入口 从vue的构建过程可以知道,web环境下,入口文件在 src/platforms/web/entry-runtime-with-compiler.js(以Runtime + Compil ...

  7. 【尺取法】Jurisdiction Disenchantment

    [尺取法]Jurisdiction Disenchantment PROBLEM 时间限制: 1 Sec 内存限制: 128 MB 题目描述 The Super League of Paragons ...

  8. 《JavaScript DOM编程艺术》学习笔记(二)

    终于开始接着写我的读书笔记了. 17.DOM有insertBefore方法,但并没有提供insertAfter()方法.不过可利用已有的DOM方法和属性编写此函数: function insertAf ...

  9. JVM之垃圾回收

    1.哪些内存需要回收?判断对象已死的方法(存活判定算法) 1.引用计数算法:难以解决对象之间相互循环引用的问题,不使用. 2.可达性分析算法:通过一系列“GC Root”对象作为起始点向下搜索,所走过 ...

  10. ABP入门系列(1)——通过模板创建MAP版本项目

    ABP入门系列目录--学习Abp框架之实操演练 一.从官网创建模板项目 进入官网下载模板项目 依次按下图选择: 输入验证码开始下载 下载提示: 二.启动项目 使用VS2015打开项目,还原Nuget包 ...