【问题标题】:ASP.NET Core mock HttpClient with custom HttpClientHandlerASP.NET Core 使用自定义 HttpClientHandler 模拟 HttpClient
【发布时间】:2021-02-25 21:57:27
【问题描述】:

我无法模拟 Moq。 通常有一个HttpClient 我会通过在基类中注入HttpClient 来模拟它,如下所示:

public class MyClass
{
    private readonly HttpClient httpClient;

    public MyClass(HttpClient httpClient)
    {
        this.httpClient = httpClient;
    }
}

但现在我的班级 MyClass 中有不同的函数,需要像这样自定义 HttpClientHandler

HttpClientHandler httpClientHandler = new HttpClientHandler();
...
using var client = new HttpClient(httpClientHandler);

如果我只是在MyClassTest 中注入HttpClientvar service = new MyClass(httpMock.Object);,那么httpClient 将被覆盖。

什么是测试我的功能而不是进行真正的 HTTP 调用的正确方法?

【问题讨论】:

  • 您应该查看IHttpClientFactory 并输入/命名HttpClient injection。
  • 1) 创建一个仅测试重载,您可以在其中传入自定义处理程序。多年来,我一直在使用这种方法并取得了巨大的成功。 2)将类抽象为 HttpClient 并注入它。从理论上讲,您的抽象应该设计为接口,并且您可以隐藏它们甚至使用 HTTP 的事实。
  • 我选择了public MyClass(HttpClientHandler httpClientHandler = null),然后用模拟调用它:var mockHttpMessageHandler = new Mock<HttpClientHandler>();。它可以完成这项工作,我只是不确定它有多“干净”或“最佳实践”。

标签: c# unit-testing asp.net-core asp.net-core-webapi moq


【解决方案1】:

我想您正在使用IHttpClientFactorytyped client 方法。这就是为什么您的 MyClass ctor 会收到 HttpClient 实例的原因。

如果您需要模拟 HttpClient,那么我建议您关注 Hamid Mosalla's advice

简而言之,有一个帮助类,它使HttpMessageHandlerSendAsync 可模拟(无需使用Moq.Protected)。

public class FakeHandler: HttpMessageHandler
{
    public virtual HttpResponseMessage Send(HttpRequestMessage request)
    {
        throw new NotImplementedException();
    }

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

您可以像这样使用这个帮助类来模拟任何HttpClient 调用:

var httpResponse = new HttpResponseMessage
{
    Content = new StringContent(JsonConvert.SerializeObject(responseObject))
};

var mockHandler = new Mock<FakeHandler> { CallBase = true };
mockHandler
    .Setup(handler => handler.Send(It.IsAny<HttpRequestMessage>()))
    .Returns(httpResponse);

var mockHttpClient = new HttpClient(mockHandler.Object);
var SUT = new MyClass(mockHttpClient);

【讨论】:

  • 这与@Andy 建议的方法相同。当我有更多时间时,我一定会看看这个,因为这看起来更“干净”。
【解决方案2】:

什么是测试我的功能而不是进行真正的 HTTP 调用的正确方法?

也许不是您想要的,但我建议您考虑一下 Andrew Lock 的智慧 - don't unit-test API/MVC controllers in ASP.NET Core

对于 .NET Core(和 .NET 5),如果您正在测试控制器类,则应避免模拟 HttpClient。

如果控制器类不是您的 SUT,我会将 HttpClient 包装在外观接口中并模拟它。

【讨论】:

  • 我正在测试控制器的底层服务类。就像从另一个页面获取和处理数据一样。然后将结果提供给控制器。控制器本身针对服务层的模拟接口进行了测试。我认为应该测试服务类,为此我需要HttpClient-mock
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-12-17
  • 2017-10-26
  • 2010-12-19
  • 1970-01-01
  • 2017-11-01
  • 1970-01-01
  • 2022-01-13
相关资源
最近更新 更多