0

How would i go about to unit test a service that makes calls to a http typed client? the examples given at https://learn.microsoft.com/en-us/aspnet/core/fundamentals/http-requests?view=aspnetcore-2.2#typed-clients do not use interface.

public GitHubService(HttpClient client)

Would creating interface for the typed client be needed for unit testing using xunit/moq? or wouldn't i need to unit test this service.

2 Answers 2

2

I don't understand what you mean with

http typed client

but if like in the example, you want to test a class which uses HttpClient, either you create a wrapper for HttpClient and pass it's interface using dependency injection (so you can mock it), or you leverage the HttpResponseMessage constructor parameter of the HttpClient.

Make the HttpClient a constructor parameter and in the test create code like the following:

var mockHttpMessageHandler = new Mock<HttpMessageHandler>();
mockHttpMessageHandler.Protected()
   .Setup<Task<HttpResponseMessage>>(
      "SendAsync",
      ItExpr.IsAny<HttpRequestMessage>(), // Customise this as you want
      ItExpr.IsAny<CancellationToken>()
   )
   // Create the response you want to return
   .ReturnsAsync(new HttpResponseMessage()
   {    
      StatusCode = HttpStatusCode.OK,
      Content = new StringContent("[{'prop1': 100,'prop2': 'value'}]"),
   });

// Create an HttpClient using the mocked message handler
var httpClient = new HttpClient(mockHttpMessageHandler.Object)
{
   BaseAddress = new Uri("http://anyurl.com/"),
};

var testedService = new MyServiceUnderTest(httpClient);

var result = await testedService.MethodUnderTest(parameters [...]);

To simplify the setup of the moq, restricting the expected HttpRequestMessage, I use this helper method.

    /// <summary>
    /// Setup the mocked http handler with the specified criteria
    /// </summary>
    /// <param name="httpStatusCode">Desired status code returned in the response</param>
    /// <param name="jsonResponse">Desired Json response</param>
    /// <param name="httpMethod">Post, Get, Put ...</param>
    /// <param name="uriSelector">Function used to filter the uri for which the setup must be applied</param>
    /// <param name="bodySelector">Function used to filter the body of the requests for which the setup must be applied</param>
    private void SetupHttpMock(HttpStatusCode httpStatusCode, string jsonResponse, HttpMethod httpMethod, Func<string, bool> uriSelector, Func<string, bool> bodySelector = null)
    {
        if (uriSelector == null) uriSelector = (s) => true;
        if (bodySelector == null) bodySelector = (s) => true;

        _messageHandlerMock
            .Protected()
            .Setup<Task<HttpResponseMessage>>("SendAsync",
                ItExpr.Is<HttpRequestMessage>(m =>
                    m.Method == httpMethod &&
                    bodySelector(m.Content.ReadAsStringAsync().Result) &&
                    uriSelector(m.RequestUri.ToString())),
                ItExpr.IsAny<CancellationToken>())
            .ReturnsAsync(new HttpResponseMessage
            {
                StatusCode = httpStatusCode,
                Content = jsonResponse == null ? null : new StringContent(jsonResponse, Encoding.UTF8, "application/json")
            });
    }
Sign up to request clarification or add additional context in comments.

1 Comment

"Typed clients Typed clients provide the same capabilities as named clients without the need to use strings as keys. The typed client approach provides IntelliSense and compiler help when consuming clients. They provide a single location to configure and interact with a particular HttpClient. For example, a single typed client might be used for a single backend endpoint and encapsulate all logic dealing with that endpoint. Another advantage is that they work with DI and can be injected where required in your app."
1

If your service class is properly designed, unit testing doesn't make a whole lot of sense. You methods should pretty much just be encapsulating calls to HttpClient, abstracting the URLs/headers/connection details/etc. You can be reasonably sure that HttpClient works, in a general sense, so there's essentially no real code to test. Again, that's assuming you're doing things right.

If you do need more complex logic, then you should have a class that has this simple service class as a dependency, and the complex logic should go there. Your service class can implement an interface, so you'd be good to go at that point.

You can of course do integration testing, on your service class, which would ensure that it functions as it should, actually calling out to the API or a facsimile thereof.

2 Comments

yes my methods are basically just making calls to the HttpClient, i think i was more thinking about when i inject the service class into another service via DI, then when i come to testing that service i now have to provide it with the http service that doesn't have interface.
Have this service class implement an interface, and then you can sub in a mock for anything that depends on it. The HttpClient dependency is contained (which is the point), so the things utilizing this service don't know and don't care about it.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.