asp.netasync-awaitintegration-testingmoqhttpcontent

Mocking HttpMessageHandler with moq - How do I get the contents of the request?


Is there a way to get the contents of the http request before deciding what kind of response I want to send back for the test? Multiple tests will use this class and each test will have multiple http requests.

This code does not compile because the lambda is not async and there is an await in it. I'm new to async-await, so I'm not sure how to resolve this. I briefly considered having multiple TestHttpClientFactories, but that would mean duplicated code, so decided against it, if possible.

Any help is appreciated.

public class TestHttpClientFactory : IHttpClientFactory
{
    public HttpClient CreateClient(string name)
    {
        var messageHandlerMock = new Mock<HttpMessageHandler>(MockBehavior.Strict);

        messageHandlerMock.Protected()
            .Setup<Task<HttpResponseMessage>>("SendAsync", ItExpr.IsAny<HttpRequestMessage>(), ItExpr.IsAny<CancellationToken>())
            .ReturnsAsync((HttpRequestMessage request, CancellationToken token) =>
            {
                HttpResponseMessage response = new HttpResponseMessage();
                var requestMessageContent = await request.Content.ReadAsStringAsync();

                // decide what to put in the response after looking at the contents of the request

                return response;
            })
            .Verifiable();

        var httpClient = new HttpClient(messageHandlerMock.Object);
        return httpClient;
    }
}

Solution

  • To take advantage of the async delegate use the Returns method instead

    public class TestHttpClientFactory : IHttpClientFactory {
        public HttpClient CreateClient(string name) {
            var messageHandlerMock = new Mock<HttpMessageHandler>(MockBehavior.Strict);
    
            messageHandlerMock.Protected()
                .Setup<Task<HttpResponseMessage>>("SendAsync", ItExpr.IsAny<HttpRequestMessage>(), ItExpr.IsAny<CancellationToken>())
                .Returns(async (HttpRequestMessage request, CancellationToken token) => {
                    
                    string requestMessageContent = await request.Content.ReadAsStringAsync();
    
                    HttpResponseMessage response = new HttpResponseMessage();
    
                    //...decide what to put in the response after looking at the contents of the request
    
                    return response;
                })
                .Verifiable();
    
            var httpClient = new HttpClient(messageHandlerMock.Object);
            return httpClient;
        }
    }
    

    Or consider creating your own handler that exposes a delegate to handle the desired behavior.

    For example

    public class DelegatingHandlerStub : DelegatingHandler {
        private readonly Func<HttpRequestMessage, CancellationToken, Task<HttpResponseMessage>> _handlerFunc;
        public DelegatingHandlerStub() {
            _handlerFunc = (request, cancellationToken) => Task.FromResult(request.CreateResponse(HttpStatusCode.OK));
        }
    
        public DelegatingHandlerStub(Func<HttpRequestMessage, CancellationToken, Task<HttpResponseMessage>> handlerFunc) {
            _handlerFunc = handlerFunc;
        }
    
        protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) {
            return _handlerFunc(request, cancellationToken);
        }
    }
    

    And used in the factory like this

    public class TestHttpClientFactory : IHttpClientFactory {
        public HttpClient CreateClient(string name) {
            var messageHandlerMock = new DelegatingHandlerStub(async (HttpRequestMessage request, CancellationToken token) => {
                    
                string requestMessageContent = await request.Content.ReadAsStringAsync();
    
                HttpResponseMessage response = new HttpResponseMessage();
    
                //...decide what to put in the response after looking at the contents of the request
    
                return response;
            });
    
            var httpClient = new HttpClient(messageHandlerMock);
            return httpClient;
        }
    }