Search code examples
c#httpclientmoq

Moq Verify() on a mocked HttpClientHandler can't access the Content object because it is disposed


I'm doing a mock HttpClient so I can unit test my code. I want to check the Content that is being posted.

I've done this:

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

and

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

and

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")));

This works fine except for the Content line. I get an error saying that the Content is Disposed.

I'm guessing this is because it's a stream.

Is there an elegant way to be able to test the Content? It would be nice to directly get access to the content because I can test for encoding problems as well.

Edit:

The HttpClient is dependency injected into a class that does this:

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;
        }
    }
}

The exception, for clarification:

System.ObjectDisposedException: Cannot access a disposed object. Object name: 'System.Net.Http.StringContent'.

This is on the m.Content.ReadAsStringAsync().Result part, because the Content is disposed.


Solution

  • I've solved this with the help of the other answers, by reading the Content in the message handler itself and storing it, then resetting the stream.

    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);
        }
    }
    

    Hopefully this will help someone else trying to read the Content when mocking a HttpClient.