Mocking HttpClient when new instance is created inside the constructor - c#

I have the following class that instantiates a new HttpClient instance in the constructor:
public class Posts
{
private readonly HttpClient _client;
public Posts(HttpClient httpClient)
{
this.httpClient = new HttpClient();
}
...
}
I've followed this blog to mock HttpClient for my test. But it's not working, since in the example, they are passing in HttpClient as a param in the constructor, whereas I am instantiating a new one every time. This results in the mock getting overwritten with what I have in the class.
The mock from the blog:
var handlerMock = new Mock<HttpMessageHandler>();
var response = new HttpResponseMessage
{
StatusCode = HttpStatusCode.OK,
Content = new StringContent(#"[{ ""id"": 1, ""title"": ""Cool post!""}, { ""id"": 100, ""title"": ""Some title""}]"),
};
handlerMock
.Protected()
.Setup<Task<HttpResponseMessage>>(
"SendAsync",
ItExpr.IsAny<HttpRequestMessage>(),
ItExpr.IsAny<CancellationToken>())
.ReturnsAsync(response);
var httpClient = new HttpClient(handlerMock.Object);
var posts = new Posts(httpClient); // passed into constructor when the class instantiated
My questions are:
How can I stop my class from overriding the mock?
Should I put HttpClient as a param in the constructor, as shown in the blog? If so, what's the benefit of it over my way?

Your subject class is not using the mock or any client that is injected into it since the constructor initializes and tightly couples to it's own instance.
Instead the class should explicitly inject the client
For example
public class Posts {
private readonly HttpClient httpClient;
public Posts(HttpClient httpClient) {
this.httpClient = httpClient;
}
//...
}
This now allows the dependency to be changed as needed when testing, and in production code you will configure your application to inject the actual client.
Ensure that HttpCLient in registered with the DI container used by the application.
services.AddHttpClient();
so the DI container knows how to resolve it when building the object graph for dependent classes

Related

Using named HttpClient from IHttpClientFactory in a custom client

I know I'll get crucified for asking this question that has been asked a million times before and I promise you that I've looked at most of those questions/answers but I'm still a bit stuck.
This is a .NET Standard 2.0 class library supporting an ASP.NET Core 6 API.
In my Program.cs I create a named HttpClient like this:
builder.Services.AddHttpClient("XYZ_Api_Client", config =>
{
var url = "https://example.com/api";
config.BaseAddress = new Uri(url);
});
I have a custom client that will use this HttpClient and I create a singleton MyCustomClient in Program.cs so that my repositories can use it. The code is below. This is where I'm stuck as I'm not sure how to pass my named HttpClient into MyCustomClient.
builder.Services.AddSingleton(new MyCustomClient(???)); // I think I need to pass the HttpClient to my CustomClient here but not sure how
And my CustomClient needs to use this HttpClient named XYZ_Api_Client to do its job:
public class MyCustomClient
{
private readonly HttpClient _client;
public MyCustomClient(HttpClient client)
{
_client = client;
}
public async Task<bool> DoSomething()
{
var result = await _client.GetAsync();
return result;
}
}
So I'm not sure how I can pass this named HttpClient into MyCustomClient in the Program.cs.
You can directly inject the IHttpClientFactory in you class, and then assign the named HttpClient to a property.
Register the factory and your custom client:
builder.Services.AddHttpClient("XYZ_Api_Client", config =>
{
var url = "https://example.com/api";
config.BaseAddress = new Uri(url);
});
// no need to pass anything, the previous line registered IHttpClientFactory in the container
builder.Services.AddSingleton<MyCustomClient>();
And then in you class:
public class MyCustomClient
{
private readonly HttpClient _client;
public MyCustomClient(IHttpClientFactory factory)
{
_client = factory.CreateClient("XYZ_Api_Client");
}
// ...
}
Or, you can pass the named instance when registering MyCustomClient
Register the factory and your custom client:
builder.Services.AddHttpClient("XYZ_Api_Client", config =>
{
var url = "https://example.com/api";
config.BaseAddress = new Uri(url);
});
// specify the factory for your class
builder.Services.AddSingleton<MyCustomClient>(sp =>
{
var factory = sp.GetService<IHttpClientFactory>();
var httpClient = factory.CreateClient("XYZ_Api_Client");
return new MyCustomClient(httpClient);
});
And then in you class:
public class MyCustomClient
{
private readonly HttpClient _client;
public MyCustomClient(HttpClient client)
{
_client = client;
}
// ...
}
You can also do this:
// register the named client with the name of the class
builder.Services.AddHttpClient("MyCustomClient", config =>
{
config.BaseAddress = new Uri("https://example.com/api");
});
// no need to specify the name of the client
builder.Services.AddHttpClient<MyCustomClient>();
What AddHttpClient<TClient>(IServiceCollection) does is
Adds the IHttpClientFactory and related services to the IServiceCollection and configures a binding between the TClient type and a named HttpClient. The client name will be set to the full name of TClient.
You can find the full documentation here.

How to inject a HttpClient<GitHubService> similar to ILogger<T>

With HttpClientFactory we can configure dependency injection to create and manage the lifetime of HttpClients, but this creates a lot of code understanding and transparency problems:
public class GitHubService
{
private readonly HttpClient _client;
private readonly string _repositoryName;
public GitHubService(HttpClient client, string repositoryName)
{
_client = client;
_repositoryName = repositoryName;
}
public async Task<IEnumerable<GitHubIssue>> GetAspNetDocsIssues()
{
var response = await _client.GetAsync(
$"/repos/aspnet/{_repositoryName}/issues?state=open&sort=created&direction=desc");
response.EnsureSuccessStatusCode();
using var responseStream = await response.Content.ReadAsStreamAsync();
return await JsonSerializer.DeserializeAsync
<IEnumerable<GitHubIssue>>(responseStream);
}
}
Then in Startup.cs we configure DI:
services.AddHttpClient<GitHubService>(c =>
{
c.BaseAddress = new Uri("https://api.github.com/");
// Github API versioning
c.DefaultRequestHeaders.Add("Accept", "application/vnd.github.v3+json");
// Github requires a user-agent
c.DefaultRequestHeaders.Add("User-Agent", "HttpClientFactory-Sample");
});
However, this raises a number of problems:
If I share the GitHubService code with someone, they will not understand that the class is using a non-standard HttpClient. To understand the logic of building requests, headers, authorization, you need to additionally study the Startup.cs class.
You might think that we are setting up an implementation of only the HttpClient for the GitHubService class. However, in fact, we are setting up dependency injection for the entire GitHubService class, without the possibility of managing its lifecycle and creation factory.
If the GitHubService class requires additional parameters in the constructor, we cannot configure them, because we do not have access to control the creation of the object.
Why not just inject a typed HttpClient<GitHubService> (like it does with ILogger<T>) that will not affect the main class and make it clear that you are not using a regular HttpClient? How can this problem be solved?
public class GitHubService
{
private readonly HttpClient<GitHubService> _client;
private readonly string _repositoryName;
public GitHubService(HttpClient<GitHubService> client, string repositoryName)
{
_client = client;
_repositoryName = repositoryName;
}
// Code removed for brevity.
}

ASP.NET Core mock HttpClient with custom HttpClientHandler

I have trouble mocking with Moq.
Normally having a HttpClient I would mock it by injecting the HttpClient in the base class like this:
public class MyClass
{
private readonly HttpClient httpClient;
public MyClass(HttpClient httpClient)
{
this.httpClient = httpClient;
}
}
But now I have different functions in my class MyClass that need a custom HttpClientHandler like this:
HttpClientHandler httpClientHandler = new HttpClientHandler();
...
using var client = new HttpClient(httpClientHandler);
If I'd simply inject a HttpClient in MyClassTest with var service = new MyClass(httpMock.Object);, then the httpClient would be overwritten.
What would be the right way to test my functions and not making a real HTTP-call?
I suppose you are using typed client approach of the IHttpClientFactory. That's why your MyClass ctor receives an HttpClient instance.
If you need to mock that HttpClient then I suggest you to follow Hamid Mosalla's advice.
In short there is a helper class, which makes HttpMessageHandler's SendAsync mockable (without the need to use the Moq.Protected).
public class FakeHandler: HttpMessageHandler
{
public virtual HttpResponseMessage Send(HttpRequestMessage request)
{
throw new NotImplementedException();
}
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
return Task.FromResult(Send(request));
}
}
You can use this helper class like this to mock any HttpClient call:
var httpResponse = new HttpResponseMessage
{
Content = new StringContent(JsonConvert.SerializeObject(responseObject))
};
var mockHandler = new Mock<FakeHandler> { CallBase = true };
mockHandler
.Setup(handler => handler.Send(It.IsAny<HttpRequestMessage>()))
.Returns(httpResponse);
var mockHttpClient = new HttpClient(mockHandler.Object);
var SUT = new MyClass(mockHttpClient);
What would be the right way to test my functions and not making a real HTTP-call?
Maybe not what you are looking for, but I suggest you consider Andrew Lock's wisdom - don't unit-test API/MVC controllers in ASP.NET Core.
For .NET Core (and .NET 5) you should avoid mocking HttpClient if you are testing a controller class.
If a controller class is not your SUT, I would wrap the HttpClient in a facade interface and mock that.

Unable to Mock HttpClient PostAsync() in unit tests

I am writing test cases using xUnit and Moq.
I am trying to mock PostAsync() of HttpClient, but I get an error.
Below is the code used for mocking:
public TestADLS_Operations()
{
var mockClient = new Mock<HttpClient>();
mockClient.Setup(repo => repo.PostAsync(It.IsAny<string>(), It.IsAny<HttpContent>())).Returns(() => Task.FromResult(new HttpResponseMessage(HttpStatusCode.OK)));
this._iADLS_Operations = new ADLS_Operations(mockClient.Object);
}
Error:
Unsupported expression: repo => repo.PostAsync(It.IsAny(),
It.IsAny()) Non-overridable members (here:
HttpClient.PostAsync) may not be used in setup / verification
expressions.
Screenshot:
Non-overridable members (here: HttpClient.PostAsync) may not be used in setup / verification expressions.
I also tried to mock the HttpClient the same way you did, and I got the same error message.
Solution:
Instead of mocking the HttpClient, mock the HttpMessageHandler.
Then give the mockHttpMessageHandler.Object to your HttpClient, which you then pass to your product code class. This works because HttpClient uses HttpMessageHandler under the hood:
// Arrange
var mockHttpMessageHandler = new Mock<HttpMessageHandler>();
mockHttpMessageHandler.Protected()
.Setup<Task<HttpResponseMessage>>("SendAsync", ItExpr.IsAny<HttpRequestMessage>(), ItExpr.IsAny<CancellationToken>())
.ReturnsAsync(new HttpResponseMessage { StatusCode = HttpStatusCode.OK });
var client = new HttpClient(mockHttpMessageHandler.Object);
this._iADLS_Operations = new ADLS_Operations(client);
Note: You will also need a
using Moq.Protected;
at the top of your test file.
Then you can call your method that uses PostAsync from your test, and PostAsync will return an HTTP status OK response:
// Act
var returnedItem = this._iADLS_Operations.MethodThatUsesPostAsync(/*parameter(s) here*/);
Advantage:
Mocking HttpMessageHandler means that you don't need extra classes in your product code or your test code.
Helpful resources:
Unit Testing with the HttpClient
How to mock HttpClient in your .NET / C# unit tests
As other answers explain, you should mock the HttpMessageHandler or the HttpClientFactory, not HttpClient. This is such a common scenario that someone created a helper library for both cases, Moq.Contrib.HttpClient.
Copying from the General Usage example for HttpClient :
// All requests made with HttpClient go through its handler's SendAsync() which we mock
var handler = new Mock<HttpMessageHandler>();
var client = handler.CreateClient();
// A simple example that returns 404 for any request
handler.SetupAnyRequest()
.ReturnsResponse(HttpStatusCode.NotFound);
// Match GET requests to an endpoint that returns json (defaults to 200 OK)
handler.SetupRequest(HttpMethod.Get, "https://example.com/api/stuff")
.ReturnsResponse(JsonConvert.SerializeObject(model), "application/json");
// Setting additional headers on the response using the optional configure action
handler.SetupRequest("https://example.com/api/stuff")
.ReturnsResponse(bytes, configure: response =>
{
response.Content.Headers.LastModified = new DateTime(2018, 3, 9);
})
.Verifiable(); // Naturally we can use Moq methods as well
// Verify methods are provided matching the setup helpers
handler.VerifyAnyRequest(Times.Exactly(3));
For HttpClientFactory :
var handler = new Mock<HttpMessageHandler>();
var factory = handler.CreateClientFactory();
// Named clients can be configured as well (overriding the default)
Mock.Get(factory).Setup(x => x.CreateClient("api"))
.Returns(() =>
{
var client = handler.CreateClient();
client.BaseAddress = ApiBaseUrl;
return client;
});
Visit Blog
There's inbuilt support to apply conditions on HttpMethod and RequestUri properties of HttpRequestMessage. This way we can mock HttpGet, HttpPost and other verbs for various paths using the EndsWith method as described below.
_httpMessageHandler.Protected()
.Setup<Task<HttpResponseMessage>>("SendAsync", true,
*// Specify conditions for httpMethod and path
ItExpr.Is<HttpRequestMessage>(req => req.Method == HttpMethod.Get
&& req.RequestUri.AbsolutePath.EndsWith($"{path}"))),*
ItExpr.IsAny<CancellationToken>())
.ReturnsAsync(new HttpResponseMessage
{
StatusCode = HttpStatusCode.OK,
Content = new StringContent("_0Kvpzc")
});
Instead of directly using an HttpClient instance in your code, use an IHttpClientFactory.
In your tests, you can then create your own implementation of IHttpClientFactory that sends back a HttpClient which connects to a TestServer.
Here's an example of what your Fake Factory could look like:
public class InMemoryHttpClientFactory: IHttpClientFactory
{
private readonly TestServer _server;
public InMemoryHttpClientFactory(TestServer server)
{
_server = server;
}
public HttpClient CreateClient(string name)
{
return _server.CreateClient();
}
}
You can then setup a TestServer in your tests and have your custom IHttpClientFactory create clients for that server:
public TestADLS_Operations()
{
//setup TestServer
IWebHostBuilder hostBuilder = new WebHostBuilder()
.Configure(app => app.Run(
async context =>
{
// set your response headers via the context.Response.Headers property
// set your response content like this:
byte[] content = Encoding.Unicode.GetBytes("myResponseContent");
await context.Response.Body.WriteAsync(content);
}));
var testServer = new TestServer(hostBuilder)
var factory = new InMemoryHttpClientFactory(testServer);
_iADLS_Operations = new ADLS_Operations(factory);
[...]
}
The problem you are having indicates tight coupling, and you can resolve it by introducing an intermediate abstraction. You might want to create a class which aggregates the HttpClient and exposes the PostAsync() method via an interface:
// Now you mock this interface instead, which is a pretty simple task.
// I suggest also abstracting away from an HttpResponseMessage
// This would allow you to swap for any other transport in the future. All
// of the response error handling could be done inside the message transport
// class.
public interface IMessageTransport
{
Task SendMessageAsync(string message);
}
// In ADLS_Operations ctor:
public ADLS_Operations(IMessageTransport messageTransport)
{
//...
}
public class HttpMessageTransport : IMessageTransport
{
public HttpMessageTransport()
{
this.httpClient = //get the http client somewhere.
}
public Task SendMessageAsync(string message)
{
return this.httpClient.PostAsync(message);
}
}

HttpClientFactory - .NET Standard - Invoke Web API

We are currently using the HttpClient to invoke the Web APIs from the MVC application.
The HttpClient is part of a static helper class as shown below
public static class ApiClient
{
private static HttpClient MyHttpClient()
{
HttpClient client = new HttpClient();
...
return client;
}
public static T HttpGet<T>(string requestUri)
{
using (var client = MyHttpClient())
{
...
}
}
}
and it is invoked from the MVC controller as given below
ApiClient.HttpGet<MyModel>("<<API URL>>");
So whenever the ApiClient is invoked, a new underlying connection will be opened which isn't the right way.
I read about HttpClientFactory and read this post and I resulted in modifying the creation logic as
private static HttpClient MyHttpClient()
{
var serviceProvider = new ServiceCollection().AddHttpClient().BuildServiceProvider();
var httpClientFactory = serviceProvider.GetService<IHttpClientFactory>();
var client = httpClientFactory.CreateClient();
//HttpClient client = new HttpClient();
...
return client;
}
Will this prevent from opening multiple connections even if invoked multiple times?
The IHttpClientFactory functionality is predicated on dependency injection, and statics are fundamentally incompatible with dependency injection. As the docs clearly show, the correct way to do this is:
public class ApiClient
{
private readonly HttpClient _client;
public ApiClient(HttpClient client)
{
_client = client;
}
...
}
And then you register this service in ConfigureServices:
services.AddHttpClient<ApiClient>(c => { ... });
The client class should not be static and there's no reason for it to be static.

Categories