Tips & tricks for unit testing in .NET Core 3: Mocking IHttpContextAccessor
Introduction
I wanted to write this article because I recently came across an interesting test case. This involved testing a factory that selected a service instance in the DI system from a service name. To be able to test this case, I had to mock the IHttpContextAccessor interface and a little bit more … Let’s see how I did it.
Test scenario
Let’s look at our test case, here we have two different instances of possible services and then according to a parameter named “name” we have to test if the instance is correctly returned:
public class UserServiceFactory : IUserServiceFactory | |
{ | |
private readonly IHttpContextAccessor _httpAccessor; | |
public UserServiceFactory(IHttpContextAccessor httpAccessor) | |
{ | |
_httpAccessor = httpAccessor; | |
} | |
public IUserServiceFactory GetService(string type) | |
{ | |
if (string.IsNullOrEmpty(type)) | |
{ | |
throw new ArgumentNullException(nameof(type)); | |
} | |
switch (type) | |
{ | |
case "Guest": | |
return _httpAccessor.HttpContext.RequestServices.GetService<GuestService>(); | |
case "Logged": | |
return _httpAccessor.HttpContext.RequestServices.GetService<LoggedService>(); | |
default: | |
throw new ArgumentException($"IUserServiceFactory type \"{type}\" not supported"); | |
} | |
} | |
} |
The solution
Let’s take a look on how IHttpContextAccessor works:
_httpAccessor.HttpContext.RequestServices.GetService<T>();
We need to create an instance (a mock) of IHttpContextAccessor , then access to an instance of HttpContext and finally access to an instance of IServiceProvider (RequestServices). Once we are done we can test if GetService returns the proper instance.
How to achieve that?
- Step 1, mock IHttpContextAccessor
- Step 2, create an instance of HttpContext with DefaultHttpContext class
- Step 3, populate an instance of IServiceCollection and register required service instances
- Step 4, build an instance of IServiceProvider from an IServiceCollection instance
- Step 5, assign IServiceProvider instance to RequestServices
Here is what the test looks like:
public class GetServiceTests | |
{ | |
private readonly UserServiceFactory _sut; | |
public GetServiceTests() | |
{ | |
var services = new ServiceCollection(); | |
services.AddScoped<GuestService>(p => new GuestService()); | |
services.AddScoped<LoggedService>(p => new LoggedService()); | |
var httpContextAccessorMock = Substitute.For<IHttpContextAccessor>(); | |
httpContextAccessorMock.HttpContext = new DefaultHttpContext | |
{ | |
RequestServices = services.BuildServiceProvider() | |
}; | |
Sut = new UserServiceFactory(HttpContextAccessorMock); | |
} | |
[Fact] | |
public void when_type_equals_Guest_should_return_instance_of_GuestService() | |
{ | |
// Arrange | |
var type = "Guest"; | |
// Act | |
var instance = Sut.GetService(type); | |
// Assert | |
instance.Should().NotBeNull().And.BeOfType<LoggedService>(); | |
} | |
[Fact] | |
public void when_type_equals_Logged_should_return_instance_of_LoggedService() | |
{ | |
// Arrange | |
var type = "Logged"; | |
// Act | |
var instance = Sut.GetService(type); | |
// Assert | |
instance.Should().NotBeNull().And.BeOfType<LoggedService>(); | |
} | |
} |
Hope this article was helpful