如何基于 URL 使用 Moq 对 HttpClient (HttpMessageHandler) 进行单元测试

How to unit test an HttpClient (HttpMessageHandler) with Moq based on the URL

致开发者, 我会模拟 HttpMessageHandler 来测试 HttpClient,我的问题是我将如何模拟基于 URL 和 Http 方法?所以响应将是方法和 URL:

的函数
Get + "http://testdoc.com/run?test=true&t2=10   => return X
Get + "http://testdoc.com/walk?test=true&t2=10   => return Y
Post + "http://testdoc.com/walk   => return Z

所有 3 个调用都会 return 不同。

我当前的单元测试捕获了所有内容:

var mockMessageHandler = new Mock<HttpMessageHandler>();
mockMessageHandler.Protected()
    .Setup<Task<HttpResponseMessage>>("SendAsync", ItExpr.IsAny<HttpRequestMessage>(), ItExpr.IsAny<CancellationToken>())
    .ReturnsAsync(new HttpResponseMessage
    {  ... });

谢谢,

问题是您告诉最小起订量设置使用任何 http 请求消息:ItExpr.IsAny<HttpRequestMessage>(),因此对于 HttpRequestMessage 的任何实例,它总是 return 相同的结果.

如果您有不同的 X 个结果,您将需要创建 X 个不同的实例:

string firstUri = "http://testdoc.com/run?test=true&t2=10";
HttpRequestMessage httpRequestMessage_1 = new HttpRequestMessage
{
    RequestUri = new Uri(firstUri),
    Method = ...,
    Content = ...,
};

而不是 ItExpr.IsAny<HttpRequestMessage>()httpRequestMessage_1 的实例,其中:

.Setup<Task<HttpResponseMessage>>("SendAsync", httpRequestMessage_1, ItExpr.IsAny<CancellationToken>())
.ReturnsAsync(new HttpResponseMessage
{  /* Something with X */ });

根据你的代码,我想出了下面的代码
模拟他们 URL.

识别的两个不同调用
var mockMessageHandler = new Mock<HttpMessageHandler>();

var content = new HttpContentMock(usersQueryResult);
content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json");

mockMessageHandler.Protected()
  .Setup<Task<HttpResponseMessage>>(
    "SendAsync",
    ItExpr.Is<HttpRequestMessage>(rm => 
      rm.RequestUri.AbsoluteUri.StartsWith("https://example.com/api/users?query=")),
    ItExpr.IsAny<CancellationToken>())
    .ReturnsAsync(
      new HttpResponseMessage
      {
        StatusCode = 200,
        Content = content
      };
    );

mockMessageHandler.Protected()
  .Setup<Task<HttpResponseMessage>>(
    "SendAsync",
    ItExpr.Is<HttpRequestMessage>(rm => 
      rm.RequestUri.AbsoluteUri.StartsWith("https://example.com/api/users/gurka/")),
    ItExpr.IsAny<CancellationToken>())
  .ReturnsAsync(
    new HttpResponseMessage
    {
      StatusCode = 201,
      Content = null, // In reality the user found but we don't care for this test.
    }
  );
}

private class HttpContentMock : HttpContent
{
  private readonly IList<AdUserDataContract> users;
  public HttpContentMock(IList<AdUserDataContract> users)
  {
    this.users = users;  }

  protected override Task SerializeToStreamAsync(Stream stream, TransportContext context)
  {
    var json = JsonSerializer.Serialize(users, typeof(AdUsersDataContract));
    var buffer = Encoding.ASCII.GetBytes(json);
    stream.Write(buffer, 0, buffer.Length);
    return Task.CompletedTask;
  }

  protected override bool TryComputeLength(out long length)
  {
    length = 1; // Well... not totally true is it?
    return true;
  }
}