Moq Verify () на поддельном HttpClientHandler не может получить доступ к объекту Content, потому что он расположен - PullRequest
0 голосов
/ 06 февраля 2020

Я делаю макет HttpClient, чтобы я мог тестировать мой код модулем. Я хочу проверить содержимое, которое публикуется.

Я сделал это:

MockHttpMessageHandler =  new Mock<FakeHttpMessageHandler>() { CallBase = true };
HttpClient = new HttpClient(MockHttpMessageHandler.Object, false);

и

 MockHttpMessageHandler.Setup(c => c.Send(It.IsAny<HttpRequestMessage>())).Returns(new HttpResponseMessage
            {
                StatusCode = HttpStatusCode.OK,
            });

и

MockHttpMessageHandler.Verify(c => c.Send(It.Is<HttpRequestMessage>(
                m => m.Content.Headers.ContentType.MediaType == "text/xml" &&
                    m.Method == HttpMethod.Post &&
                    m.RequestUri.ToString() == "http://www.test.com/" &&
                    m.Content.ReadAsStringAsync().Result == "TestContent")));

Это прекрасно работает, за исключением строки Content. Я получаю сообщение об ошибке, в котором говорится, что Контент удален.

Я предполагаю, что это потому, что это поток.

Есть ли элегантный способ проверить Контент? Было бы неплохо получить прямой доступ к контенту, потому что я могу также проверить наличие проблем с кодированием.

Редактировать:

HttpClient - это зависимость, внедренная в класс, который делает это:

public class MyHttpSenderClass
{
    HttpClient _httpClient; // DI populates this

    //...

    public async Task<HttpResponseMessage> ComposeAndsendHttpRequestMessage(string url, string payload, string mediaType, string method)
    {    var httpRequestMessage = new HttpRequestMessage(method, new Uri(url));

        httpRequestMessage.Content = new StringContent(payload);

        httpRequestMessage.Content.Headers.ContentType = new MediaTypeHeaderValue(mediaType);

        using (_httpClient)
        {
            // I want to test this httpRequestMessage is correct
            var responseMessage = await _httpClient.SendAsync(httpRequestMessage);

            return responseMessage;
        }
    }
}

Исключение, для пояснения:

System.ObjectDisposedException: Невозможно получить доступ к удаленному объекту. Имя объекта: 'System. Net .Http.StringContent'.

Это часть m.Content.ReadAsStringAsync().Result, поскольку Контент удаляется.

Ответы [ 2 ]

0 голосов
/ 06 февраля 2020

Я решил это с помощью других ответов, прочитав Содержимое в самом обработчике сообщений и сохранив его, а затем сбросив поток.

public class FakeHttpMessageHandler : HttpMessageHandler
{ 
    public string Content { get; set; }

    public virtual HttpResponseMessage Send(HttpRequestMessage request)
    {
        throw new NotImplementedException("Use Moq to overrite this method");
    }

    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
    {
        MemoryStream msInput = new MemoryStream();
        await request.Content.CopyToAsync(msInput);
        byte[] byteInput = msInput.ToArray();
        msInput.Seek(0, SeekOrigin.Begin);

        Content = Encoding.UTF8.GetString(byteInput);

        return Send(request);
    }
}

Надеюсь, это поможет кому-то другому, пытающемуся читать Контент при издевательстве над HttpClient.

0 голосов
/ 06 февраля 2020

Непосредственная проблема заключается в том, что один и тот же объект ответа используется для всех вызовов. Это не будет работать, потому что объекты HttpRequestMessage и HttpResponseMessage удаляются как можно скорее. такая же проблема возникнет, если макет использовал тот же DbConnection, DbContext или любой другой одноразовый объект, который утилизируется тестируемым кодом.

Returns может принимать лямбду, а не только один объект, поэтому проблему можно решить с помощью:

.Returns(reqMsg=>new HttpResponseMessage
            {
                StatusCode = HttpStatusCode.OK,
            });

или

.Returns(_ =>new HttpResponseMessage
            {
                StatusCode = HttpStatusCode.OK,
            });
...