Moq: How to check a class using Nunit with an internal HttpClient?

I run my tests inside nUnit and usually can mock dependencies and then return specific values ​​or throw errors.

I have a class that is like an internal HttpClient, and I would like to test the class what are my options.

here is my code, its not complete so as not to flood the message. As you can see, I use HttpClient internally and is not injected as a dependency. The class throws a series of user exceptions, I would like Moq to do it otherwise, I need to pass a REAL username and passwords that will give me status codes that I should have excluded.

Does anyone have any ideas? If I cannot mock httpclient, then I can never verify my class that it is throwing exceptions.

Do I really need to change the HttpClient to a constructor dependency?

public bool ItemsExist(string itemValue) { var relativeUri = string.Format(UrlFormatString, itemValue.ToUpper()); var uri = new Uri(new Uri(this.baseUrl), relativeUri); using (var client = new HttpClient()) { client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", this.encodedCredentials); client.DefaultRequestHeaders.Accept.Add( new MediaTypeWithQualityHeaderValue("application/json")); var response = client.GetAsync(uri).Result; switch (response.StatusCode) { case HttpStatusCode.Unauthorized: // DO something here throw new CustomAuthorizationException(); case HttpStatusCode.Forbidden: throw new CustomAuthenticationException(); } return true; 
+6
source share
3 answers

You cannot unit test it like that. As you mentioned: HttpClient is a dependency, and therefore it must be introduced.

Personally, I would create my own IHttpClient interface implemented by HttpClientWrapper , which wraps around System.Net.HttpClient . IHttpClient will be passed as a dependency on your contructor object.

As follows, HttpClientWrapper cannot be tested per unit. However, I would write a couple of integration tests to make sure that the shell is well written.

Edit

IHttpClient does not have to be a "valid" interface for HttpClient . It should only be an interface that suits your needs. It can have as many or as few methods as possible.

Portray this: HttpClient allows you to do many things. But in your project, you only call the GetAsync(uri).Result , nothing more.

Given this scenario, you should write the following interface and implementation:

 interface IHttpClient { HttpResponseMessage Get(string uri); } class HttpClientWrapper : IHttpClient { private readonly HttpClient _client; public HttpClientWrapper(HttpClient client) { _client = client; } public HttpResponseMessage Get(string uri) { return _client.GetAsync(new Uri(uri)).Result; } } 

So, as I said earlier, the interface should suit your needs. You do not need to wrap the WHOLE HttpClient class.

Obviously, you then moq your object as follows:

 var clientMock = new Mock<IHttpClient>(); //setup mock var myobj = new MyClass(clientMock.object); 

And to create the actual object:

 var client = new HttpClientWrapper(new HttpClient()); var myobj = new MyClass(client ); 

Edit2

HE! And don't forget that IHttpClient should also extend the IDisposable interface, very important!

+11
source

Let me suggest a slightly easier solution, without having to abstract / wrap the httpclient, which I think works just fine with mocking frameworks.

You need to create a class for a fake HttpMessageHandler, for example:

 public class FakeHttpMessageHandler : HttpMessageHandler { public virtual HttpResponseMessage Send(HttpRequestMessage request) { throw new NotImplementedException("Rember to setup this method with your mocking framework"); } protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken) { return Task.FromResult(Send(request)); } } 

This created HttpMessageHandler can be used when creating an instance of HttpClient:

 var msgHandler = new Mock<FakeHttpMessageHandler>() { CallBase = true }; var fakeclient = new HttpClient(msgHandler.Object); 

And you can customize the methods (here using Moq):

 msgHandler.Setup(t => t.Send(It.Is<HttpRequestMessage>( msg => msg.Method == HttpMethod.Post && msg.RequestUri.ToString() == "http://test.te/item/123"))) .Returns(new HttpResponseMessage(System.Net.HttpStatusCode.NotFound)); 

Now you can use user fakeclient if necessary.

+19
source

Another option is to use Flurl [disclosure: I am the author] - a library for creating and calling URLs. It includes test assistants that make faking all of HTTP incredibly easy. No need for wrapper interfaces.

To get started, your HTTP code will look something like this:

 using Flurl; using Flurl.Http; ... try { var response = this.baseUrl .AppendPathSegment(relativeUri) .WithBasicAuth(username, password) .WithHeader("Accept", "application/json") .GetAsync().Result; return true; } catch (FlurlHttpException ex) { // Flurl throws on unsuccessful responses. Null-check ex.Response, // then do your switch on ex.Response.StatusCode. } 

Now for testing fun:

 using Flurl.Http.Testing; ... [Test] public void ItemsExists_SuccessResponse() { // kick Flurl into test mode - all HTTP calls will be faked and recorded using (var httpTest = new HttpTest()) { // arrange test.RespondWith(200, "{status:'ok'}"); // act sut.ItemExists("blah"); // assert test.ShouldHaveCalled("http://your-url/*"); } } 

Get it on NuGet:

 PM> Install-Package Flurl.Http 
+2
source

Source: https://habr.com/ru/post/953576/


All Articles