Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/266.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 使用Moq模拟HttpClient_C#_Unit Testing_Moq_Mstest_Dotnet Httpclient - Fatal编程技术网

C# 使用Moq模拟HttpClient

C# 使用Moq模拟HttpClient,c#,unit-testing,moq,mstest,dotnet-httpclient,C#,Unit Testing,Moq,Mstest,Dotnet Httpclient,我想对使用HttpClient的类进行单元测试。我们在类构造函数中注入了HttpClient对象 public class ClassA : IClassA { private readonly HttpClient _httpClient; public ClassA(HttpClient httpClient) { _httpClient = httpClient; } public async Task<HttpRespon

我想对使用
HttpClient
的类进行单元测试。我们在类构造函数中注入了
HttpClient
对象

public class ClassA : IClassA
{
    private readonly HttpClient _httpClient;

    public ClassA(HttpClient httpClient)
    {
        _httpClient = httpClient;
    }

    public async Task<HttpResponseMessage> SendRequest(SomeObject someObject)
    {
        //Do some stuff

        var request = new HttpRequestMessage(HttpMethod.Post, "http://some-domain.in");

        //Build the request

        var response = await _httpClient.SendAsync(request);

        return response;
    }
}

我们如何解决这个问题?

特定的重载方法不是虚拟的,因此无法被Moq覆盖

public Task<HttpResponseMessage> SendAsync(HttpRequestMessage request);
然而,模仿HttpClient并不像内部消息处理程序那样简单

我建议使用带有自定义消息处理程序存根的具体客户机,这将在伪造请求时提供更大的灵活性

下面是一个委托处理程序存根的示例

public class DelegatingHandlerStub : DelegatingHandler {
    private readonly Func<HttpRequestMessage, CancellationToken, Task<HttpResponseMessage>> _handlerFunc;
    public DelegatingHandlerStub() {
        _handlerFunc = (request, cancellationToken) => Task.FromResult(request.CreateResponse(HttpStatusCode.OK));
    }

    public DelegatingHandlerStub(Func<HttpRequestMessage, CancellationToken, Task<HttpResponseMessage>> handlerFunc) {
        _handlerFunc = handlerFunc;
    }

    protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) {
        return _handlerFunc(request, cancellationToken);
    }
}

Propper使用HttpClient进行模拟是一项艰苦的工作,因为它是在大多数人在dotnet中进行单元测试之前编写的。有时,我会设置一个存根HTTP服务器,根据与请求url匹配的模式返回固定响应,这意味着您测试的是真正的HTTP请求,而不是模拟,而是本地主机服务器。使用使这变得非常简单,并且运行速度足够快,可以满足我的大部分单元测试需求

因此,与其使用
http://some-domain.in
在某些端口上使用本地主机服务器设置,然后:

var server = FluentMockServer.Start(/*server and port can be setup here*/);
server.Given(
      Request.Create()
      .WithPath("/").UsingPost()
   )
   .RespondWith(
       Response.Create()
       .WithStatusCode(200)
       .WithHeader("Content-Type", "application/json")
       .WithBody("{'attr':'value'}")
   );

您可以找到更多详细信息,Moq可以模拟受保护的方法,例如HttpMessageHandler上的SendAsync,您可以在其构造函数中提供给HttpClient

public class ClassA : IClassA
{
    private readonly HttpClient _httpClient;

    public ClassA(HttpClient httpClient)
    {
        _httpClient = httpClient;
    }

    public async Task<HttpResponseMessage> SendRequest(SomeObject someObject)
    {
        //Do some stuff

        var request = new HttpRequestMessage(HttpMethod.Post, "http://some-domain.in");

        //Build the request

        var response = await _httpClient.SendAsync(request);

        return response;
    }
}
var mockHttpMessageHandler=new Mock();
mockHttpMessageHandler.Protected()
.Setup(“sendsync”、ItExpr.IsAny()、ItExpr.IsAny())
.ReturnsAsync(新的HttpResponseMessage
{
StatusCode=HttpStatusCode.OK
});
var client=新的HttpClient(mockHttpMessageHandler.Object);

抄袭自

我最近不得不模仿HttpClient,我使用了。这正是我所需要的,而且使用简单,所以我想我应该把它扔掉

以下是一般用法的示例:

// All requests made with HttpClient go through its handler's SendAsync() which we mock
var handler = new Mock<HttpMessageHandler>();
var client = handler.CreateClient();

// A simple example that returns 404 for any request
handler.SetupAnyRequest()
    .ReturnsResponse(HttpStatusCode.NotFound);

// Match GET requests to an endpoint that returns json (defaults to 200 OK)
handler.SetupRequest(HttpMethod.Get, "https://example.com/api/stuff")
    .ReturnsResponse(JsonConvert.SerializeObject(model), "application/json");

// Setting additional headers on the response using the optional configure action
handler.SetupRequest("https://example.com/api/stuff")
    .ReturnsResponse(bytes, configure: response =>
    {
        response.Content.Headers.LastModified = new DateTime(2018, 3, 9);
    })
    .Verifiable(); // Naturally we can use Moq methods as well

// Verify methods are provided matching the setup helpers
handler.VerifyAnyRequest(Times.Exactly(3));
//使用HttpClient发出的所有请求都会通过其处理程序的SendAsync()进行模拟
var handler=newmock();
var client=handler.CreateClient();
//一个简单的示例,对于任何请求都返回404
handler.SetupAnyRequest()
.ReturnsResponse(HttpStatusCode.NotFound);
//将GET请求与返回json的端点匹配(默认为200 OK)
handler.SetupRequest(HttpMethod.Get)https://example.com/api/stuff")
.ReturnsResponse(JsonConvert.SerializeObject(模型),“应用程序/json”);
//使用可选的配置操作在响应上设置其他标头
handler.SetupRequest(“https://example.com/api/stuff")
.ReturnsResponse(字节,配置:响应=>
{
response.Content.Headers.LastModified=新日期时间(2018年3月9日);
})
.Verifiable();//当然,我们也可以使用最小起重量法
//验证是否提供了与设置帮助程序匹配的方法
handler.VerifyAnyRequest(Times.justice(3));
有关更多信息,请查看作者的博客文章

var server = FluentMockServer.Start(/*server and port can be setup here*/);
server.Given(
      Request.Create()
      .WithPath("/").UsingPost()
   )
   .RespondWith(
       Response.Create()
       .WithStatusCode(200)
       .WithHeader("Content-Type", "application/json")
       .WithBody("{'attr':'value'}")
   );
// All requests made with HttpClient go through its handler's SendAsync() which we mock
var handler = new Mock<HttpMessageHandler>();
var client = handler.CreateClient();

// A simple example that returns 404 for any request
handler.SetupAnyRequest()
    .ReturnsResponse(HttpStatusCode.NotFound);

// Match GET requests to an endpoint that returns json (defaults to 200 OK)
handler.SetupRequest(HttpMethod.Get, "https://example.com/api/stuff")
    .ReturnsResponse(JsonConvert.SerializeObject(model), "application/json");

// Setting additional headers on the response using the optional configure action
handler.SetupRequest("https://example.com/api/stuff")
    .ReturnsResponse(bytes, configure: response =>
    {
        response.Content.Headers.LastModified = new DateTime(2018, 3, 9);
    })
    .Verifiable(); // Naturally we can use Moq methods as well

// Verify methods are provided matching the setup helpers
handler.VerifyAnyRequest(Times.Exactly(3));