作者: Anthony Giretti http://anthonygiretti.com/
译者: 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
来源: https://www.cnblogs.com/lwqlun/p/10215929.html