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.
Very help post. Thank you!