How to use FakeItEasy with HttpClient in unit test?

I am trying to figure out how to use FakeItEasy with HttpClient, given the following code:

public Foo(string key, HttpClient httpClient = null) { .. } public void DoGet() { .... if (_httpClient == null) { _httpClient = new HttpClient(); } var response = _httpClient.GetAsync("user/1); } public void DoPost(foo Foo) { if (_httpClient == null) { _httpClient = new HttpClient(); } var formData = new Dictionary<string, string> { {"Name", "Joe smith"}, {"Age", "40"} }; var response = _httpClient.PostAsync("user", new FormUrlEncodedContent(formData)); } 

So I'm not sure how to use FakeItEasy to fake the HttpClient GetAsync and PostAsync .

production code will not pass in HttpClient, but unit test will pass in a fake instance made by FakeItEasy.

eg.

 [Fact] public void GivenBlah_DoGet_DoesSomething() { // Arrange. var httpClient A.Fake<HttpClient>(); // <-- need help here. var foo = new Foo("aa", httpClient); // Act. foo.DoGet(); // Assert.... } 

UPDATE:

I understand that FiE (and most mocking packages) works on interfaces or virtual methods. So, for this question, let's just assume that the GetAsync and PostAsync are virtual ... please :)

+7
c # unit-testing fakeiteasy
source share
4 answers

Here is my (more or less) generic FakeHttpMessageHandler.

 public class FakeHttpMessageHandler : HttpMessageHandler { private HttpResponseMessage _response; public static HttpMessageHandler GetHttpMessageHandler( string content, HttpStatusCode httpStatusCode ) { var memStream = new MemoryStream(); var sw = new StreamWriter( memStream ); sw.Write( content ); sw.Flush(); memStream.Position = 0; var httpContent = new StreamContent( memStream ); var response = new HttpResponseMessage() { StatusCode = httpStatusCode, Content = httpContent }; var messageHandler = new FakeHttpMessageHandler( response ); return messageHandler; } public FakeHttpMessageHandler( HttpResponseMessage response ) { _response = response; } protected override Task<HttpResponseMessage> SendAsync( HttpRequestMessage request, CancellationToken cancellationToken ) { var tcs = new TaskCompletionSource<HttpResponseMessage>(); tcs.SetResult( _response ); return tcs.Task; } } 

Here is an example of how it is used in one of my tests, which expects some kind of JSON to return.

 const string json = "{\"success\": true}"; var messageHandler = FakeHttpMessageHandler.GetHttpMessageHandler( json, HttpStatusCode.BadRequest ); var httpClient = new HttpClient( messageHandler ); 

You will now inject httpClient into your test class (using whatever injection mechanism you prefer), and when GetAsync is called your messageHandler , it will return the result you told him about.

+11
source

I did something similar when I needed to interact with the Gravatar service. I tried using fakes / mocks, but found that this is not possible with HttpClient. Instead, I came up with a custom class, HttpMessageHandler, that allows me to preload the expected response along these lines:

 using System.Net.Http; using System.Threading; using System.Threading.Tasks; namespace Tigra.Gravatar.LogFetcher.Specifications { /// <summary> /// Class LoggingHttpMessageHandler. /// Provides a fake HttpMessageHandler that can be injected into HttpClient. /// The class requires a ready-made response message to be passed in the constructor, /// which is simply returned when requested. Additionally, the web request is logged in the /// RequestMessage property for later examination. /// </summary> public class LoggingHttpMessageHandler : DelegatingHandler { internal HttpResponseMessage ResponseMessage { get; private set; } internal HttpRequestMessage RequestMessage { get; private set; } public LoggingHttpMessageHandler(HttpResponseMessage responseMessage) { ResponseMessage = responseMessage; } protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { RequestMessage = request; return Task.FromResult(ResponseMessage); } } } 

Then my test context setup looks something like this:

 public class with_fake_gravatar_web_service { Establish context = () => { MessageHandler = new LoggingHttpMessageHandler(new HttpResponseMessage(HttpStatusCode.OK)); GravatarClient = new HttpClient(MessageHandler); Filesystem = A.Fake<FakeFileSystemWrapper>(); Fetcher = new GravatarFetcher(Committers, GravatarClient, Filesystem); }; protected static LoggingHttpMessageHandler MessageHandler; protected static HttpClient GravatarClient; protected static FakeFileSystemWrapper Filesystem; } 

Then, here is an example of a test (specification) that uses it:

 [Subject(typeof(GravatarFetcher), "Web service")] public class when_fetching_imagaes_from_gravatar_web_service : with_fake_gravatar_web_service { Because of = () => { var result = Fetcher.FetchGravatars(@"c:\"); // This makes the web request Task.WaitAll(result.ToArray()); //"http://www.gravatar.com/avatar/".md5_hex(lc $email)."?d=404&size=".$size; UriPath = MessageHandler.RequestMessage.RequestUri.GetComponents(UriComponents.Path, UriFormat.Unescaped); }; It should_make_request_from_gravatar_dot_com = () => MessageHandler.RequestMessage.RequestUri.Host.ShouldEqual("www.gravatar.com"); It should_make_a_get_request = () => MessageHandler.RequestMessage.Method.ShouldEqual(HttpMethod.Get); // see https://en.gravatar.com/site/check/ tim@tigranetworks.co.uk It should_request_the_gravatar_hash_for_tim_long = () => UriPath.ShouldStartWith("avatar/df0478426c0e47cc5e557d5391e5255d"); static string UriPath; } 

You can see the full source at http://stash.teamserver.tigranetworks.co.uk/users/timlong/repos/tigra.gravatar.logfetcher/browse

+3
source

FakeItEasy, like most mocking libraries, does not create proxies for non-abstract components. In the case of HttpClient , the GetAsync and HttpClient methods are neither virtual nor abstract , so you cannot directly create them. See https://github.com/FakeItEasy/FakeItEasy/wiki/What-can-be-faked .

In this case, you need another abstraction as a dependency - one of which HttpClient can execute, but there may also be other implementations, including mocks / stubs.

+2
source

This does not answer your question directly, but I wrote a library some time ago that provides an API for query / response execution. It is quite flexible and supports ordered / unordered matching, as well as a custom backup system for unrivaled queries.

It is available on GitHub here: https://github.com/richardszalay/mockhttp

0
source

All Articles