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.