In ASP.NET Core unit tests, if you want to mock HttpContext.Features.Get<SomeType>()
, here's the trick.
Problem
I have my Error page code that will get exception detail infomation, to do that, I use HttpContext.Features.Get<IExceptionHandlerPathFeature>()
.
public void OnGet()
{
var requestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier;
var exceptionFeature = HttpContext.Features.Get<IExceptionHandlerPathFeature>();
if (exceptionFeature is not null)
{
// Get which route the exception occurred at
var routeWhereExceptionOccurred = exceptionFeature.Path;
// Get the exception that occurred
var exceptionThatOccurred = exceptionFeature.Error;
_logger.LogError($"Error: {routeWhereExceptionOccurred}, " +
$"client IP: {HttpContext.Connection.RemoteIpAddress}, " +
$"request id: {requestId}", exceptionThatOccurred);
}
RequestId = requestId;
}
Now I want to unit test this piece of code. Usually I will use DefaultHttpContext
instance in unit tests that requires an HttpContext
for a page or controller. But I found the Features
property is readonly on the HttpContext
class. So there's no way to assign values that we mocked to this property.
namespace Microsoft.AspNetCore.Http
{
public abstract class HttpContext
{
protected HttpContext();
//
// Summary:
// Gets the collection of HTTP features provided by the server and middleware available
// on this request.
public abstract IFeatureCollection Features { get; }
// ...
}
}
Solution
First, prepare the mock as usual. In my case, I set up IFeatureCollection.Get()
method to return my desired object.
var mockIFeatureCollection = _mockRepository.Create<IFeatureCollection>();
mockIFeatureCollection.Setup(p => p.Get<IExceptionHandlerPathFeature>())
.Returns(new ExceptionHandlerFeature
{
Path = "/996/icu",
Error = new("Too much fubao")
});
httpContextMock.Setup(p => p.Features).Returns(mockIFeatureCollection.Object);
Then, in order to assign values to HttpContext.Features
, we can't use DefaultHttpContext
this time. We have to create a mock for HttpContext
. And set up the Features
property to return our mocked IFeatureCollection
object.
var httpContextMock = _mockRepository.Create<HttpContext>();
httpContextMock.Setup(p => p.Features).Returns(mockIFeatureCollection.Object);
Now, run the unit test, we can see values are coming up correctly.
Kyle
Very help post. Thank you!
Steve
Instead of mocking, you could do the below. You can't change the Feature collection itself, but you can change its contents.