Reputation: 634
In order to properly test a custom ExceptionFilterAttribute in .NET Core 3 we need to mock an ExceptionContext in an Xunit test using Moq. How can I do this?
Exception Filter:
public class CustomExceptionFilter : ExceptionFilterAttribute
{
public override OnException(ExceptionContext context)
{
/* Code here that handles exceptions */
}
}
I've looked everywhere and can't figure out a good way to mock these things in a way that ensures nothing throws an exception due to other missing dependencies. How can I mock ExceptionContext
easily?
Upvotes: 4
Views: 4653
Reputation: 634
The problem lies in the fact that if you are trying to mock ExceptionContext
in order to return different exception types, you actually want to mock an exception and then use that mock exception when instantiating the ExceptionContext
.
First, to instantiate the ExceptionContext
for filters in a test you need to be able to instantiate the ActionContext
. The ActionContext
is not relevant to our test but is required by the dependency tree so we must instantiate it with as little customization as possible:
var actionContext = new ActionContext()
{
HttpContext = new DefaultHttpContext(),
RouteData = new RouteData(),
ActionDescriptor = new ActionDescriptor()
};
After you are able to instantiate the ActionContext
you can instantiate the ExceptionContext. Here we are also mocking the exception that is being used to build the ExceptionContext
. This is the most important step as this is the value which changes the behavior we are testing.
// The stacktrace message and source member variables are virtual and so we can stub them here.
var mockException = new Mock<Exception>();
mockException.Setup(e => e.StackTrace)
.Returns("Test stacktrace");
mockException.Setup(e => e.Message)
.Returns("Test message");
mockException.Setup(e => e.Source)
.Returns("Test source");
var exceptionContext = new ExceptionContext(actionContext, new List<FilterMetadata>())
{
Exception = mockException.Object
};
Doing it this way allows you to adequately test the behavior of an exception filter when given different exception types.
Full code:
[Fact]
public void TestExceptionFilter()
{
var actionContext = new ActionContext()
{
HttpContext = new DefaultHttpContext(),
RouteData = new RouteData(),
ActionDescriptor = new ActionDescriptor()
};
// The stacktrace message and source member variables are virtual and so we can stub them here.
var mockException = new Mock<Exception>();
mockException.Setup(e => e.StackTrace)
.Returns("Test stacktrace");
mockException.Setup(e => e.Message)
.Returns("Test message");
mockException.Setup(e => e.Source)
.Returns("Test source");
// the List<FilterMetadata> here doesn't have much relevance in the test but is required
// for instantiation. So we instantiate a new instance of it with no members to ensure
// it does not effect the test.
var exceptionContext = new ExceptionContext(actionContext, new List<FilterMetadata>())
{
Exception = mockException.Object
};
var filter = new CustomExceptionFilter();
filter.OnException(exceptionContext);
// Assumption here that your exception filter modifies status codes.
// Just used as an example of how you can assert in this test.
context.HttpContext.Response.StatusCode.Should().Be(500,
"Because the response code should match the exception thrown.");
}
Upvotes: 9