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
使用的是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
ASP.NET Core中如何针对一个使用HttpClient对象的类编写单元测试的更多相关文章
- 如何在ASP.NET Core中实现一个基础的身份认证
注:本文提到的代码示例下载地址> How to achieve a basic authorization in ASP.NET Core 如何在ASP.NET Core中实现一个基础的身份认证 ...
- [转]如何在ASP.NET Core中实现一个基础的身份认证
本文转自:http://www.cnblogs.com/onecodeonescript/p/6015512.html 注:本文提到的代码示例下载地址> How to achieve a bas ...
- ASP.NET Core中如果Response.HasStarted已经为true,就不能更改Response.Cookies和Response.Headers等属性的值了
最近我在ASP.NET Core中做了一个中间件CustomizedMiddleware,要说该中间件的功能也很简单,其实就是往HttpResponse中添加一个Cookie而已,但是我将添加Cook ...
- gRPC在 ASP.NET Core 中应用学习(二)
前言: 上一篇文章中简单的对gRPC进行了简单了解,并实现了gRPC在ASP.NET Core中服务实现.客户端调用:那么本篇继续对gRPC的4中服务方法定义.其他使用注意点进一步了解学习 一.gRP ...
- 3、带你一步一步学习ASP.NET Core中的配置之Configuration
如果你是刚接触ASP.NET Core的学习的话,你会注意到:在ASP.NET Core项目中,看不到.NET Fraemwork时代中的web.config文件和app.config文件了.那么你肯 ...
- 在ASP.NET Core中实现一个Token base的身份认证
注:本文提到的代码示例下载地址> How to achieve a bearer token authentication and authorization in ASP.NET Core 在 ...
- 在ASP.NET Core中通过EF Core实现一个简单的全局过滤查询
前言 不知道大家是否和我有同样的问题: 一般在数据库的设计阶段,会制定一些默认的规则,其中有一条硬性规定就是一定不要对任何表中的数据执行delete硬删除操作,因为每条数据对我们来说都是有用的,并且是 ...
- 从零搭建一个IdentityServer——聊聊Asp.net core中的身份验证与授权
OpenIDConnect是一个身份验证服务,而Oauth2.0是一个授权框架,在前面几篇文章里通过IdentityServer4实现了基于Oauth2.0的客户端证书(Client_Credenti ...
- ASP.NET Core 中的那些认证中间件及一些重要知识点
前言 在读这篇文章之间,建议先看一下我的 ASP.NET Core 之 Identity 入门系列(一,二,三)奠定一下基础. 有关于 Authentication 的知识太广,所以本篇介绍几个在 A ...
随机推荐
- python基础其他
目录: 一.Python为什么受欢迎的本质,知道的人寥寥无几? 二.PyCharm的模板设置 三.pycharm的一些快捷键 四.PyCharm最新2018激活 五.python虚拟环境--virtu ...
- Analysis of Web.xml in Hello1 project
一.web.xml文件介绍 The web.xml file contains several elements that are required for a Facelets applicatio ...
- 封装PDO函数
funPDO.php <?php /** * @title: 封装PDO函数 * * @Features: * 1. 封装 SELECT ,INSERT,DELETE,UPDATE 操作 @do ...
- CentOS7部分调优命令
一般CentOS的磁盘空间占用最大的就是日志文件,日志文件主要保存在./log目录里,因此通过以下命令可以检查目录的大小. du -ah --max-depth=1 du命令的一些常用参数: -a或- ...
- jquery ajax中success与complete的执行顺序
jquery ajax中success与complete的执行顺序 jquery中各个事件执行顺序如下: 1.ajaxStart(全局事件) 2.beforeSend 3.ajaxSend(全局事件) ...
- CGI、FastCGI、PHP-FPM联系与区别(理解总结自其他博文)
参考:http://blog.csdn.net/tyrantbear/article/details/52077321 参考:http://mp.weixin.qq.com/s?src=11& ...
- Hive表生成函数explode讲解
Hive中的表分析函数接受零个或多个输入,然后产生多列或多行输出. 1.explode函数 explode函数以array类型数据输入,然后对数组中的数据进行迭代,返回多行结果,一行一个数组元素值 A ...
- 五、JAVA反射、线程
第五节:Java反射.线程 线程 1.进程:进程是程序的基本执行实体,进程是线程的容器. 线程:被称为轻量进程,是程序执行流的最小单元.线程是进程中的一个实 ...
- LeetCode 15 3Sum [sort] <c++>
LeetCode 15 3Sum [sort] <c++> 给出一个一维数组,找出其中所有和为零的三元组(元素集相同的视作同一个三元组)的集合. C++ 先自己写了一发,虽然过了,但跑了3 ...
- Nginx的gzip
webpack compression-webpack-plugin => .gz CompressionPlugin = require("compression-webpack- ...