Как модульно протестировать HttpClient (HttpMessageHandler) с Moq на основе URL-адреса

#c# #unit-testing #httpclient #moq

#c# #модульное тестирование #httpclient #moq

Вопрос:

Для разработчиков я бы издевался над HttpMessageHandler, чтобы протестировать HttpClient, мой вопрос в том, как я мог бы издеваться на основе метода URL и Http? Таким образом, ответ будет зависеть от метода и URL-адреса:

 Get   "http://testdoc.com/run?test=trueamp;t2=10   => return X
Get   "http://testdoc.com/walk?test=trueamp;t2=10   => return Y
Post   "http://testdoc.com/walk   => return Z
  

Все 3 вызова будут возвращать что-то другое.

Мой текущий модульный тест улавливает все:

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

Спасибо,

Ответ №1:

Основываясь на вашем коде, я придумал приведенный ниже код
, который имитирует два разных вызова, распознаваемых по их 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;
  }
}
  

Ответ №2:

Проблема в том, что вы указываете настройке moq использовать любое сообщение http-запроса с помощью: ItExpr.IsAny<HttpRequestMessage>() , поэтому для любого экземпляра HttpRequestMessage он всегда будет возвращать один и тот же результат.

Если у вас разные X результатов, вам нужно будет создать X разных экземпляров:

 string firstUri = "http://testdoc.com/run?test=trueamp;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 */ });
  

Комментарии:

1. Несколько макетов для нескольких тестовых случаев попробуют конкретное сообщение запроса и посмотрят, проверены ли значения. Вы ответили на него, спасибо.

2. Рад, что это помогло вам