【问题标题】:How to mock IHttpClientFactory using typed clients如何使用类型化客户端模拟 IHttpClientFactory
【发布时间】:2019-06-17 15:15:10
【问题描述】:

我正在尝试在我的应用程序中正确添加HttpClient,同时还允许模拟客户端工厂以进行单元测试。我使用this 作为资源。

在我的 Startup.cs

services.AddHttpClient<IUploader, Uploader>()
    .ConfigurePrimaryHttpMessageHandler(() =>
    {
        if (somecheckismet)
            return new HttpClientHandler() { ServerCertificateCustomValidationCallback = delegate { return true; } };
        return new HttpClientHandler();
    });

还有我的 Uploader.cs

public class Uploader : IUploader
{
    private readonly HttpClient m_httpClient;

    public Uploader(HttpClient client)
    {
        m_httpClient = client;
    }

    public async Task<string> UploadData(string url, Dictionary<string, string> data)
    {
        HttpResponseMessage result;
        try
        {
            result = await m_httpClient.PostAsync(url, new FormUrlEncodedContent(data));
            if (result.StatusCode != HttpStatusCode.OK)
            {
                return "Some error message;
            }
            else
            {
                return null; // Success!
            }
        }
        catch (Exception ex)
        {
            return "Uh-oh!";
        }
    }
}

如您所见,我正在尝试将 HttpClient 依赖注入到 Uploader 中。但是,既然我已经这样做了,我该如何对Uploader 进行单元测试?以前,它可以依赖注入 HttpClientHandler ,它可以被模拟掉。但是,现在由于我尝试使用IHttpClientFactory,看来我必须注入HttpClient

任何帮助将不胜感激。

【问题讨论】:

标签: c# .net-core dotnet-httpclient


【解决方案1】:

似乎您可以继续模拟 HttpClientHandler 并使用它来创建 HttpClient

public void TestMethod() 
{
    var handler = // Mock the HttpClientHandler;
    var client = new HttpClient(handler);
    var uploader = new Uploader(client);

    // Draw the rest of the owl
}

【讨论】:

  • 如果你需要一个真正的工厂生产的httpclient怎么办?我正在尝试测试我的代码是否使用 httpclient 工厂正确设置了 polly 策略。为了测试它,我需要从 real 工厂获取一个 _real httpclient,但我希望该客户端有一个模拟处理程序,这样我就可以人为地通过/失败我的网络请求。
【解决方案2】:

您实际上不需要最小起订量 HttpClientFactoryHttpClientFactory 只是一个帮助类,它从池中返回 HttpClient 实例。在单元测试场景中,您将直接设置HttpClient 实例。您的 startup.cs 永远不会进入图片,因为您没有启动整个应用程序。

您的 Uploader 类已经设置为单元测试,因为您正在传递您的 HttpClient 实例。要围绕HttpClient 进行单元测试,您不需要最小量出HttpClient 实例,而是替换HttpClient 使用的HttpMessageHandler。 这是一个link 的所有细节的精彩帖子

直接从文章中提取的代码

// ARRANGE
var handlerMock = new Mock<HttpMessageHandler>(MockBehavior.Strict);
handlerMock
   .Protected()
   // Setup the PROTECTED method to mock
   .Setup<Task<HttpResponseMessage>>(
      "SendAsync",
      ItExpr.IsAny<HttpRequestMessage>(),
      ItExpr.IsAny<CancellationToken>()
   )
   // prepare the expected response of the mocked http call
   .ReturnsAsync(new HttpResponseMessage()
   {
      StatusCode = HttpStatusCode.OK,
      Content = new StringContent("[{'id':1,'value':'1'}]"),
   })
   .Verifiable();

// use real http client with mocked handler here
var httpClient = new HttpClient(handlerMock.Object)
{
   BaseAddress = new Uri("http://test.com/"),
};

var subjectUnderTest = new MyTestClass(httpClient);

// ACT
var result = await subjectUnderTest
   .GetSomethingRemoteAsync('api/test/whatever');

// ASSERT
result.Should().NotBeNull(); // this is fluent assertions here...
result.Id.Should().Be(1);

// also check the 'http' call was like we expected it
var expectedUri = new Uri("http://test.com/api/test/whatever");

handlerMock.Protected().Verify(
   "SendAsync",
   Times.Exactly(1), // we expected a single external request
   ItExpr.Is<HttpRequestMessage>(req =>
      req.Method == HttpMethod.Get  // we expected a GET request
      && req.RequestUri == expectedUri // to this uri
   ),
   ItExpr.IsAny<CancellationToken>()
);

【讨论】:

  • 如果你需要一个真正的工厂生产的httpclient怎么办?我正在尝试测试我的代码是否使用 httpclient 工厂正确设置了 polly 策略。为了测试它,我需要从 real 工厂获得一个 real httpclient,但我希望该客户端有一个 mocked 处理程序,所以我可以人为地通过/失败我的网络请求。
猜你喜欢
  • 1970-01-01
  • 2019-05-18
  • 1970-01-01
  • 2020-12-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多