0
votes

How would i go about to unit test a service that makes calls to a http typed client? the examples given at https://docs.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

2 Answers

1
votes

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")
            });
    }
1
votes

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.