Creating stub for `private static readonly` field - c#

Due on Improper Instantiation problem it is recommended to create private static readonly instance of HttpClient.
Due on lack of time I have injected mocked client into test method with client as their parameter.
The problem is how can I in simple way inject mock into private static readonly HttpClient field of SingleHttpClientInstanceController?

how can I in simple way inject mock into private static readonly
HttpClient field of SingleHttpClientInstanceController?
Answer: There is no simple way.
Suggestion:
Abstract the resource behind an accessor
public interface IHttpClientAccessor {
HttpClient HttpClient { get; }
}
and inject that into the dependent controller.
public class SingleHttpClientInstanceController : ApiController {
private readonly HttpClient HttpClient;
public SingleHttpClientInstanceController(IHttpClientAccessor httpClientAccessor) {
HttpClient = httpClientAccessor.HttpClient;
}
// This method uses the shared instance of HttpClient for every call to GetProductAsync.
public async Task<Product> GetProductAsync(string id) {
var hostName = HttpContext.Current.Request.Url.Host;
var result = await HttpClient.GetStringAsync(string.Format("http://{0}:8080/api/...", hostName));
return new Product { Name = result };
}
}
The same should also be done for accessing HttpContext which is what was recently introduced in Asp.Net-Core's IHttpContextAccessor
An implementation of the IHttpClientAcessor can look something like this
public class HttpClientAccessor : IHttpClientAccessor {
static readonly Lazy<HttpClient> client = new Lazy<HttpClient>(() => new HttpClient());
public HttpClient HttpClient { get { return client.Value; } }
}
So now for tests you can inject mock of the dependency.
If using a DI container remember to register the accessor as a singleton as well.

Related

Can I reuse Httpclient in Integration tests in ASP.NET Core?

I have an Asp.net core 6 Web api.
I make Integration tests with xUnit following the pattern here. WebApplicationFactory creates a client which the tests can use to make calls to the application instance.
Now, xUnit runs the class constructor for every test. To share a member between all tests in a test class, you need to use a xUnit class fixture. This is the example from MSDN:
public class IndexPageTests :
IClassFixture<CustomWebApplicationFactory<RazorPagesProject.Startup>>
{
private readonly HttpClient _client;
private readonly CustomWebApplicationFactory<RazorPagesProject.Startup>
_factory;
public IndexPageTests(
CustomWebApplicationFactory<RazorPagesProject.Startup> factory)
{
_factory = factory;
_client = factory.CreateClient(new WebApplicationFactoryClientOptions
{
AllowAutoRedirect = false
});
}
I don't understand - why they put the CreateClient method in the constructor? Obviously, it will create a new client for every test. Why don't we create a separate Fixture class, create the client there and just inject it in the Test class?
For example:
public class TestFixture
{
public TestFixture()
{
var factory = new CustomWebApplicationFactory<RazorPagesProject.Startup>();
Client = factory.CreateClient(new WebApplicationFactoryClientOptions
{
AllowAutoRedirect = false
});
}
public HttpClient Client { get; }
}
public class TestClass : IClassFixture<TestFixture>
{
private readonly TestFixture _fixture;
public TestClass(TestFixture fixture)
{
_fixture = fixture;
}
[Fact]
public async Task FirstTest()
{
// use the fixture client directly
await _fixture.Client.GetAsync("..../url");
}
}
Does anyone see a problem with this approach? Memory leaks or other issues?
Is the fact that we instantiate CustomWebApplicationFactory<RazorPagesProject.Startup>() in the fixture constructor and not disposing it afterwards - causing memory problems, etc?

Dependency injection class that is resolved by async provider

I have an api client that in its constructor accepts access token
public class ApiClient : IApiClient
{
public ApiClient(string accessToken)
}
To get that api token I need to create instance of another class and call method GetTokenAsync()
public class TokenClient
{
public Task<string> GetTokenAsync();
}
now I want to inject IApiClient to my TestService
public class TestService
{
public TestService(IApiClient client)
}
However it is not possible register instance in this way
services.AddScoped<IApiClient,ApiClient>(async x=>
{
tokenClient = x.GetService<TokenClient>();
var token = await tokenClient.GetTokenAsync();
return new ApiClient(token);
}
to overcome that I have created ApiClientProvider class
public class ApiClientProvider()
{
public async Task<IApiClient> GetClientAsync()
{
tokenClient = x.GetService<TokenClient>();
var token = await tokenClient.GetTokenAsync();
return new ApiClient(token);
}
}
and now I inject it to TestService
but in every method I have to use
IApiClient apiClient= await _apiClientProvider.GetClientAsync();
I don not like this code, I prefer when dependencies are injected and not resolved in every function, however I do not see any way around. Can you advise if this can be moved to registration or maybe it shouldn't go there.
You should change your design slightly.
Rather than inject a string into your ApiClient, inject an ITokenClient:
public interface ITokenClient
{
Task<string> GetTokenAsync();
}
public class TokenClient : ITokenClient
{
public Task<string> GetTokenAsync() { }
}
public class ApiClient : IApiClient
{
readonly Task<string> tokenTask;
public ApiClient(ITokenClient tokenClient)
{
// Initiate the token request on construction
tokenTask = tokenClient.GetTokenAsync();
}
public async Task SomeMethod()
{
string token = await tokenTask;
// Use your token..
}
}
Any methods that rely on the token must be async, but as your calling an API they're likely to be async anyway.
Using this method, you simply need to register your clients with the container and no factories / provider types are required.

How to implement IHttpFactoryClient in examples of Typed HttpClient?

In every example I've seen, including the Microsoft ones here and here, the author's explain the improvements made by IHttpClientFactory over HttpClient and give examples of how to use it simply out-of-the-box or in Named form. But then they all seem to mention that utilizing the Typed form really is best for its structure, usability, and more. The reasons make sense for our use case.
Though like the links provided above, there isn't a single line of code instantiating, injecting, or using IHttpClientFactory in the involvement of creating a Typed HttpClient (or Service as a Client). You create the Typed Client:
public class GitHubService
{
public HttpClient Client { get; }
public GitHubService(HttpClient client)
{
and then you consume it in some model or controller:
public TypedClientModel(GitHubService gitHubService)
{
_gitHubService = gitHubService;
}
public async Task OnGet()
{
try
{
LatestIssues = await _gitHubService.GetAspNetDocsIssues();
}
I'm extremely confused. My team was originally hitting roadblocks trying to mock (with Moq) Typed clients for unit testing, and the conclusion that we came to after many great resources was that mocking was exponentially easier with IHttpClientFactory. But I haven't found a single example that explicitly uses IHttpClientFactory with Typed clients.
The framework will use ITypedHttpClientFactory to create the HttpClient to be injected into the typed client. This is happening under the hood when the typed client is configured like so:
services.AddHttpClient<ICatalogService, CatalogService>()
If we peek into AddHttpClient, we can see that it'll attempt to create a transient version of IHttpClientFactory called ITypedHttpClientFactory
services.TryAdd(ServiceDescriptor.Transient(typeof(ITypedHttpClientFactory<>), typeof(DefaultTypedHttpClientFactory<>)));
Typed clients do also allow for abstracted clients
public class GitHubService :IGitHubService { // <-- NOTE THE INTERFACE
HttpClient client
public GitHubService(HttpClient client) {
this.client = client;
}
//...
Where the interface is registered along with its implementation using AddHttpClient
services.AddHttpClient<IGitHubService, GitHubService>();
and used accordingly
//...
private readonly IGitHubService gitHubService;
public TypedClientModel(IGitHubService gitHubService) {
this.gitHubService = gitHubService;
}
public async Task OnGet() {
try {
LatestIssues = await gitHubService.GetAspNetDocsIssues();
}
//...
The advantage here is that You decouple from 3rd party dependencies (framework concerns) since you are the one in control of the typed client and its abstraction.
This will allow for easier mocking of the typed client abstraction when testing in isolation.
With IHttpClientFactory you have three options:
IHttpClientFactory
Usage
public class SampleController
{
private readonly IHttpClientFactory _clientFactory;
public SampleController(IHttpClientFactory clientFactory)
{
_clientFactory = clientFactory;
}
}
Mocking
//Arrange
var mockClientFactory = new Mock<IHttpClientFactory>();
var mockMessageHandler = new Mock<HttpMessageHandler>();
mockMessageHandler.Protected()
.Setup<Task<HttpResponseMessage>>("SendAsync", ItExpr.IsAny<HttpRequestMessage>(), ItExpr.IsAny<CancellationToken>())
.ReturnsAsync(expectedResponseMessage);
var client = new HttpClient(mockMessageHandler.Object);
mockClientFactory
.Setup(_ => _.CreateClient(It.IsAny<string>()))
.Returns(client);
Named client
Usage
public class SampleController
{
private readonly HttpClient _client;
public SampleController(IHttpClientFactory clientFactory)
{
_client = clientFactory.CreateClient("SampleProxy");
}
}
Mocking
As an alternative we can avoid the usage of Moq.Protected by using a custom HttpMessageHandler
public class FakeMessageHandler: HttpMessageHandler
{
public virtual HttpResponseMessage Send(HttpRequestMessage request)
{
throw new NotImplementedException();
}
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
return Task.FromResult(Send(request));
}
}
//Arrange
var mockClientFactory = new Mock<IHttpClientFactory>();
var mockMessageHandler = new Mock<FakeMessageHandler> { CallBase = true };
mockMessageHandler
.Setup(handler => handler.Send(It.IsAny<HttpRequestMessage>()))
.Returns(expectedResponseMessage);
var client = new HttpClient(mockMessageHandler.Object);
mockClientFactory
.Setup(_ => _.CreateClient("SampleProxy"))
.Returns(client);
Typed client
Usage
public class SampleController
{
private readonly ISampleClient _client;
public SampleController(ISampleClient client)
{
_client = client;
}
}
Mocking
//Arrange
var clientMock = new Mock<ISampleClient>();
clientMock
.Setup(client => client.GetXYZ(It.IsAny<SampleRequest>()))
.ReturnsAsync(expectedSampleResponse);
var SUT = new SampleController(clientMock.Object);

How to pass/inject more than one HttpClient parameter to a typed HttpClientClass?

I want to register a typed HttpClient as here Microsoft docs.
Basically, the approach should be
services.AddHttpClient();
normally the pattern of these classes receive only the HttpClient class as a parameter and you implement the logic to call the endpoint. In my case, I need to use 2 HttpClient inside my MyHttpClient, one that pings the endpoint and the other one that talks with an IdentityProvider to discover the refreshEndpoints to refresh my cookies.
public class MyHttpClient : IMyHttpClient
{
public MyHttpClient (HttpClient httpClient,
HttpClient refreshHttpClient)
{
}
}
If I am trying to resolve from a controller an IMyHttpClient, I get an error saying it can't resolve an HttpClient.
In the GitHub code on line 43 AddHttpClient you can see that is calling
DefaultTypedHttpClientFactory.
If you go to the implementation of the DefaultTypedHttpClientFactory implementation you will notice that is a generic type. And when it calls CreateClient it only passes one parameter to the constructor on line 39.
The only workaround I am seeing here is to not create a typed client and register a normal class that receives an IHttpClientFactory and create and configure my clients on the fly, not as typed.
Any other idea?
You can't. You'll either need to inject another service layer or IHttpClientFactory directly
Another service
public class MyRefreshClient
{
private readonly HttpClient _httpClient;
public MyRefreshClient(HttpClient httpClient)
{
_httpClient = httpClient;
}
...
}
public class MyHttpClient : IMyHttpClient
{
private readonly HttpClient _httpClient;
private readonly MyRefreshClient _refreshClient;
public MyHttpClient(HttpClient httpClient, MyRefreshClient refreshClient)
{
_httpClient = httpClient;
_refreshClient = refreshClient;
}
}
Then:
services.AddHttpClient<MyRefreshClient>(c => { ... });
services.AddHttpClient<MyHttpClient>(c => { ... });
Inject IHttpClientFactory (and use named clients):
public class MyHttpClient : IMyHttpClient
{
private readonly HttpClient _httpClient;
private readonly HttpClient _refreshClient;
public MyHttpClient(IHttpClientFactory httpClientFactory)
{
_httpClient = httpClientFactory.CreateClient("MyHttpClient");
_refreshClient = httpClientFactory.CreateClient("MyRefreshClient");
}
}
Then:
services.AddHttpClient("MyHttpClient", c => { ... });
services.AddHttpClient("MyRefreshClient", c=> { ... });

Unit Tests and incapsulation

For example, I have a class, working with HttpClient
public class DomainActions : IDomainActions
{
private readonly HttpClient _client;
private readonly IConfiguration _configuration;
public DomainActions(IConfiguration configuration)
{
_configuration = configuration;
_client = new HttpClient()
{
BaseAddress = new Uri(_configuration.GetSection("DomainRegistration:BaseAddress").Value)
};
_client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _configuration.GetSection("DomainRegistration:Token").Value);
}
public async Task<List<DomainDto>> GetDomainListAsync()
{
var responseMessage = await _client.GetAsync("domains");
return await ProcessingDomainListResponseAsync(responseMessage);
}
then we resolve it by the following way:
services.AddTransient<IConfiguration>(....);
services.AddTransient<IDomainActions, DomainActions>();
and client class:
public class AddMxRecordToRegistrator
{
protected readonly IDomainActions domainActions;
public AddMxRecordToRegistrator(IDomainActions domainActions )
{
this.domainActions = domainActions ;
}
public async Task CreateDomainRecordAsync()
{
await domainActions.CreateDomainRecordAsync(queueItem.DomainForRegistration.DomainName, new DomainRegistrationCore.Models.DomainRecordDto
{
Content = queueItem.MxRecord,
Name = String.Empty,
Priority = 0,
Ttl = 3600,
Type = DomainRecordType.MX.ToString(),
Regions = null
});
ok, it works fine.
Right now, I want to create unit test for AddMxRecordToRegistrator class , but I don't want to use real httpClient. How to do it? Of course, I can add one more dependency:
public class DomainActions : IDomainActions
{
private readonly HttpClient _client;
private readonly IConfiguration _configuration;
public DomainActions(IConfiguration configuration, HttpMessageHandler httpMessageHandler)
{
_configuration = configuration;
_client = new HttpClient(httpMessageHandler)
{
BaseAddress = new Uri(_configuration.GetSection("DomainRegistration:BaseAddress").Value)
};
_client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _configuration.GetSection("DomainRegistration:Token").Value);
}
public DomainActions(IConfiguration configuration) : this(configuration, new HttpClientHandler())
{
}
public async Task<List<DomainDto>> GetDomainListAsync()
{
var responseMessage = await _client.GetAsync("domains");
return await ProcessingDomainListResponseAsync(responseMessage);
}
then modify DI composition root:
services.AddTransient<IConfiguration>(....);
services.AddTransient<HttpMessageHandler>(....);
services.AddTransient<IDomainActions, DomainActions>();
but then why client part (in our case composition root) should know anything about internal detail of DomainActions only because we need to create unit test? It like we violate incapsulation for unit tests. How to implement it correctly?
To expand on the comment from #CamiloTerevinto, AddMxRecordToRegistrator should depend on IDomainActions via dependency injection, i.e. that interface should be the argument passed to its constructor.
From an encapsulation perspective, AddMxRecordToRegistrator shouldn't know that DomainActions depends on IConfiguration or HttpMessageHandler. It shouldn't even know that DomainActions exists, because that's a concrete class, and AddMxRecordToRegistrator should depend on interfaces, not concrete classes.
but then why client part (in our case composition root) should know
anything about internal detail of DomainActions only because we need
to create unit test?
Composition root is only place in application which will "know" about all lower level dependencies.
"Composition" root's role is to compose required classes with runtime implementations.
Class AddMxRecordToRegistrator clearly depends on abstraction IDomainActions, so for unit testing AddMxRecordToRegistrator you just pass fake implementation of IDomainActions.

Categories

Resources