【问题标题】:How to moq HttpClientExtensions method "PostAsJsonAsync"?如何最小起订量 HttpClientExtensions 方法“PostAsJsonAsync”?
【发布时间】:2021-02-17 13:55:16
【问题描述】:

我需要围绕 PostAsJsonAsync 编写单元测试用例,这是 HttpClientExtensions 中的扩展方法。我找不到任何简单的起订量方法。

谁能帮帮我。

【问题讨论】:

  • 你不能模拟扩展方法。您需要“模拟”扩展方法正在扩展的东西,在本例中为 HttpClient
  • 扩展方法实际上只是带有编译器语法糖的静态方法,由于静态方法不能被覆盖,答案就是你不能。解决方案是创建一个实现接口的小型包装类,然后模拟该接口。
  • Plug:也可以使用我写的这个库,这样可以更方便地测试使用HttpClient的代码:github.com/justeat/httpclient-interception

标签: c# asp.net asp.net-mvc unit-testing


【解决方案1】:

我能想到的两种方法:

  1. 使用 Moles 之类的框架:https://www.microsoft.com/en-us/research/project/moles-isolation-framework-for-net/ 这样你就可以用你自己的替换扩展或任何其他方法,返回你想要的值

  2. 这是我的首选方式。在这种情况下,将服务包装在代理接口中。您可能会在其他地方发现它被称为适配器模式,但在我看来,您只是在抽象操作并代理数据。

所以创建IHttpClientProxy 和相应的具体实现,它将使用您喜欢的任何扩展。将IHttpClientProxy 传递给您的班级并根据需要模拟它。

如 cmets 中所述,模拟框架无法以这种方式模拟静态方法。像 Moq 这样的框架只能模拟虚拟或抽象方法(接口方法本质上是抽象的)来指导更好的设计。

【讨论】:

    【解决方案2】:

    我正在使用 NSubstitute 在 HttpClient 上模拟这个扩展方法,它似乎在不使用 Moles 或适配器的情况下工作正常。

    public class ApiClientTests
    {
    
        private HttpClient _client;
        private string _url;
        private ModelDto _testModel;
    
        public void ApiClientTests()
        {
            _client = Substitute.For<HttpClient>();
            _client.BaseAddress = new Uri("http://fakeUrl/api/")
    
            _url = "Models/";
            _testModel = new ModelDto
            {
                Id = 1,
                Name = "Model Name",
                Description = "Model Description",
                Outputs = new Dictionary<string, ModelOutputDto>(),
                Parameters = new Dictionary<string, ModelParamDto>(),
                Active = true
            };
    
        }
    
        [Fact]
        public async Task CreateItemAsync_ValidResponseCode_ReturnsNewResourceUri()
        {
            // Arrange
    
            var returnUri = $"{_client.BaseAddress}{_url}";
    
            var returnThis = new HttpResponseMessage(System.Net.HttpStatusCode.Created);
            returnThis.Headers.Location = new Uri(returnUri);
    
            _client.PostAsJsonAsync(_url, _testModel).ReturnsForAnyArgs(Task.FromResult(returnThis));
    
            var apiClient = new ApiClient<ModelDto>(_client);
    
            // Act
            var result = await apiClient.CreateItemAsync(_testModel, _url);
    
            // Assert
            await _client.ReceivedWithAnyArgs().PostAsJsonAsync(_url, _testModel);
            result.AbsoluteUri.Should().BeEquivalentTo(returnUri);
        }
    

    【讨论】:

      【解决方案3】:

      对我来说,问题是我不明白 PostAsJsonAsync 实际上是一种最终在处理程序上调用 SendAsync 的便捷方法。您找到的许多与模拟 HTTP 客户端相关的答案都是准确的。要点是您确实在模拟 HttpMessageHandler,并使用具体的 HttpClient。缺少的部分是您仍然需要在“SendAsync”上执行设置和验证,而不是 PostAsJsonAsync。这是我用来测试 PostAsJsonAsync 的代码,因为 SendAsync 在处理程序上被调用:

      // Arrange
      
      var handlerMock = new Mock<HttpMessageHandler>(MockBehavior.Strict);
      handlerMock
          .Protected()
          .Setup<Task<HttpResponseMessage>>(
              "SendAsync",
              ItExpr.IsAny<HttpRequestMessage>(),
              ItExpr.IsAny<CancellationToken>()
          )
          .ReturnsAsync(new HttpResponseMessage()
          {
              StatusCode = HttpStatusCode.OK,
              Content = new StringContent("[{'id':1,'value':'1'}]"),
          })
          .Verifiable();
      
      // new up a real HttpClient, passing in the mocked handler
      var httpClient = new HttpClient(handlerMock.Object)
      {
          BaseAddress = new Uri("http://example.com")
      };
      
      // replace with an instance of your actual concrete client
      var yourClient = new YourClientHere(httpClient);
      
      // Act
      // perform the action on yourClient that makes the PostAsJsonAsync call
      
      // Assert
      handlerMock.Protected().Verify(
          "SendAsync",
          Times.Exactly(accessRights.Count),
          ItExpr.IsAny<HttpRequestMessage>(),
          ItExpr.IsAny<CancellationToken>()
      );
      

      这篇文章帮助我正确设置了一切:https://gingter.org/2018/07/26/how-to-mock-httpclient-in-your-net-c-unit-tests/

      【讨论】:

        猜你喜欢
        • 2021-08-22
        • 2022-01-13
        • 1970-01-01
        • 1970-01-01
        • 2015-10-28
        • 1970-01-01
        • 1970-01-01
        • 2010-09-25
        • 1970-01-01
        相关资源
        最近更新 更多