Search code examples
c#.netmockingdotnet-httpclientmicrosoft-fakes

How to pass in a mocked HttpClient in a .NET test?


I have a service which uses Microsoft.Net.Http to retrieve some Json data. Great!

Of course, I don't want my unit test hitting the actual server (otherwise, that's an integration test).

Here's my service ctor (which uses dependency injection...)

public Foo(string name, HttpClient httpClient = null)
{
...
}

I'm not sure how I can mock this with ... say .. Moq or FakeItEasy.

I want to make sure that when my service calls GetAsync or PostAsync .. then i can fake those calls.

Any suggestions how I can do that?

I'm -hoping- i don't need to make my own Wrapper .. cause that's crap :( Microsoft can't have made an oversight with this, right?

(yes, it's easy to make wrappers .. i've done them before ... but it's the point!)


Solution

  • You can replace the core HttpMessageHandler with a fake one. Something that looks like this...

    public class FakeResponseHandler : DelegatingHandler
    {
        private readonly Dictionary<Uri, HttpResponseMessage> _FakeResponses = new Dictionary<Uri, HttpResponseMessage>();
    
        public void AddFakeResponse(Uri uri, HttpResponseMessage responseMessage)
        {
            _FakeResponses.Add(uri, responseMessage);
        }
    
        protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
        {
            if (_FakeResponses.ContainsKey(request.RequestUri))
            {
                return Task.FromResult(_FakeResponses[request.RequestUri]);
            }
            else
            {
                return Task.FromResult(new HttpResponseMessage(HttpStatusCode.NotFound) { RequestMessage = request });
            }
        }
    }
    

    and then you can create a client that will use the fake handler.

    var fakeResponseHandler = new FakeResponseHandler();
    fakeResponseHandler.AddFakeResponse(new Uri("http://example.org/test"), new HttpResponseMessage(HttpStatusCode.OK));
    
    var httpClient = new HttpClient(fakeResponseHandler);
    
    var response1 = await httpClient.GetAsync("http://example.org/notthere");
    var response2 = await httpClient.GetAsync("http://example.org/test");
    
    Assert.Equal(response1.StatusCode,HttpStatusCode.NotFound);
    Assert.Equal(response2.StatusCode, HttpStatusCode.OK);