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!)
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);
I know that this is an old question but I stumbled with it during a search on this topic and found a very nice solution to make testing HttpClient
easier.
It is available via nuget:
https://github.com/richardszalay/mockhttp
PM> Install-Package RichardSzalay.MockHttp
Here is a quick look on the usage:
var mockHttp = new MockHttpMessageHandler();
// Setup a respond for the user api (including a wildcard in the URL)
mockHttp.When("http://localost/api/user/*")
.Respond("application/json", "{'name' : 'Test McGee'}"); // Respond with JSON
// Inject the handler or client into your application code
var client = new HttpClient(mockHttp);
var response = await client.GetAsync("http://localost/api/user/1234");
// or without await: var response = client.GetAsync("http://localost/api/user/1234").Result;
var json = await response.Content.ReadAsStringAsync();
// No network connection required
Console.Write(json); // {'name' : 'Test McGee'}
More info on the github project page. Hope this can be useful.
I would just make a small change to @Darrel Miller's answer, which is using Task.FromResult to avoid the warning about an async method expecting an await operator.
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 });
}
}
}
You might take a look at Microsoft Fakes, especially at the Shims
-section. With them, you're able to modify the behaviours of your HttpClient itself. Prerequisite is, that you're using VS Premium or Ultimate.