欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

ASP.NET Core针对一个使用HttpClient对象的类编写单元测试详解

程序员文章站 2022-06-23 22:05:46
介绍 几年前,微软引入了httpclient类来替代httpwebrequest来发送web请求。这个新的类更易于使用,更加简洁,更具有异步性,且易于扩展。 htt...

介绍

几年前,微软引入了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 (本地下载

好了,以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对的支持。

原文地址: how to unit test a class that consumes an httpclient with ihttpclientfactory in asp.net core?

作者: anthony giretti