How can you unit test create an action filter in ASP.NET Web Api?

I wanted to add an action filter to my service to handle adding link data to the response message. I found that I need to mock the HttpActionExecutedContext, but it's a complex class to make fun of, how do you deal with testing the Action Filter?

+51
unit-testing asp.net-web-api action-filter
Aug 07 2018-12-12T00:
source share
6 answers

You can create a fake for the HttpActionExecutedContext as shown below:

 public static HttpActionContext CreateActionContext(HttpControllerContext controllerContext = null, HttpActionDescriptor actionDescriptor = null) { HttpControllerContext context = controllerContext ?? ContextUtil.CreateControllerContext(); HttpActionDescriptor descriptor = actionDescriptor ?? new Mock<HttpActionDescriptor>() { CallBase = true }.Object; return new HttpActionContext(context, descriptor); } public static HttpActionExecutedContext GetActionExecutedContext(HttpRequestMessage request, HttpResponseMessage response) { HttpActionContext actionContext = CreateActionContext(); actionContext.ControllerContext.Request = request; HttpActionExecutedContext actionExecutedContext = new HttpActionExecutedContext(actionContext, null) { Response = response }; return actionExecutedContext; } 

I just copied and pasted this code from the source code of the ASP.NET Web API: ContextUtil . Here are some examples of how they tested some of the built-in filters:

ActionFilterAttributeTest is a test class for ActionFilterAttribute , which is an abstract class, but you get this idea.

+57
Aug 07 '12 at 18:15
source share

Just new.

 private HttpActionContext CreateExecutingContext() { return new HttpActionContext { ControllerContext = new HttpControllerContext { Request = new HttpRequestMessage() } }; } private HttpActionExecutedContext CreateExecutedContextWithStatusCode(HttpStatusCode statusCode) { return new HttpActionExecutedContext { ActionContext = new HttpActionContext { ControllerContext = new HttpControllerContext { Request = new HttpRequestMessage() } }, Response = new HttpResponseMessage { StatusCode = statusCode, Content = new StringContent("blah") } }; } 
+24
Dec 17 '14 at 2:30 p.m.
source share

I had the same problem when trying to test the custom unhandled exception filter that I created.

It did the trick. Many new products and a very long line of code.

 var httpActionExecutedContext = new HttpActionExecutedContext( new HttpActionContext( new HttpControllerContext( new HttpConfiguration(), Substitute.For<IHttpRouteData>(), new HttpRequestMessage()), Substitute.For<HttpActionDescriptor>()), null); 

NSubstiute has been used, but any mocking structure of your choice that handles abstract base classes will be fine.

Hope this helps

+10
Jul 11 '13 at 16:16
source share

I also hit my head against a brick wall. I tried contextUtil but kept getting a null reference exception. I learned how to call actionFilter in this Notabene post . The actionFilter is not called when using the Mock filter instance, I had to use the real object. NTN

In particular:

 var httpActionContext = new HttpActionContext { ControllerContext = new HttpControllerContext { Request = requestMessage } }; //call filter var filter = new FooFilter(); filter.OnActionExecuting(httpActionContext); 
+6
Feb 24 '14 at 12:31
source share

Here is a working example from 2018 (.NET Framework 4.5.1). It uses an ExceptionFilterAttribute, but it should be similar for other FilterAttributes.

 [Test] public void MyTest() { var request = new HttpRequestMessage(HttpMethod.Get, new Uri("http://www.google.com")); var response = new HttpResponseMessage(); // This next line is necessary to avoid the following error // if you call 'context.Request.CreateResponse(...)' inside the filter: // System.InvalidOperationException: The request does not have an associated configuration object or the provided configuration was null. // Discovered from https://stackoverflow.com/a/44447355/3312114 request.SetConfiguration(new HttpConfiguration()); var context = ContextUtil.GetActionExecutedContext(request, response); _myFilter.OnException(context); // Execute your methods Assert.AreEqual(HttpStatusCode.InternalServerError, context.Response.StatusCode); // Make your assertions } 

Then just copy the ContextUtil class into your test project. @Thomasb's comment on @tugberk's answer suggests that the latest code is on Codeplex . While this comment was in 2014, so there may be even later code, the 2014 code worked for me (in January 2018), while the source related code did not work. I copied a later version below for convenience. Just put this in a new file.

 internal static class ContextUtil { public static HttpControllerContext CreateControllerContext(HttpConfiguration configuration = null, IHttpController instance = null, IHttpRouteData routeData = null, HttpRequestMessage request = null) { HttpConfiguration config = configuration ?? new HttpConfiguration(); IHttpRouteData route = routeData ?? new HttpRouteData(new HttpRoute()); HttpRequestMessage req = request ?? new HttpRequestMessage(); req.SetConfiguration(config); req.SetRouteData(route); HttpControllerContext context = new HttpControllerContext(config, route, req); if (instance != null) { context.Controller = instance; } context.ControllerDescriptor = CreateControllerDescriptor(config); return context; } public static HttpActionContext CreateActionContext(HttpControllerContext controllerContext = null, HttpActionDescriptor actionDescriptor = null) { HttpControllerContext context = controllerContext ?? ContextUtil.CreateControllerContext(); HttpActionDescriptor descriptor = actionDescriptor ?? CreateActionDescriptor(); descriptor.ControllerDescriptor = context.ControllerDescriptor; return new HttpActionContext(context, descriptor); } public static HttpActionContext GetHttpActionContext(HttpRequestMessage request) { HttpActionContext actionContext = CreateActionContext(); actionContext.ControllerContext.Request = request; return actionContext; } public static HttpActionExecutedContext GetActionExecutedContext(HttpRequestMessage request, HttpResponseMessage response) { HttpActionContext actionContext = CreateActionContext(); actionContext.ControllerContext.Request = request; HttpActionExecutedContext actionExecutedContext = new HttpActionExecutedContext(actionContext, null) { Response = response }; return actionExecutedContext; } public static HttpControllerDescriptor CreateControllerDescriptor(HttpConfiguration config = null) { if (config == null) { config = new HttpConfiguration(); } return new HttpControllerDescriptor() { Configuration = config, ControllerName = "FooController" }; } public static HttpActionDescriptor CreateActionDescriptor() { var mock = new Mock<HttpActionDescriptor>() { CallBase = true }; mock.SetupGet(d => d.ActionName).Returns("Bar"); return mock.Object; } } 
+2
Jan 18 '18 at 2:36
source share

Link https://stackoverflow.com/a/1678828/

You can create an HTTPActionContext yourself using the following:

  _ctx = new HttpActionContext { ControllerContext = new HttpControllerContext() { Request = new HttpRequestMessage() } }; _ctx.Request.Properties[System.Web.Http.Hosting.HttpPropertyKeys.HttpConfigurationKey] = new HttpConfiguration(); 

A trick without setting up the Request.Properties entry, it will show an error:

The request has no associated configuration object, or the provided configuration was null.

This may be an omission on the part of the designers, since you can set the HTTPConfiguration in the HTTPActionContext constructor!

0
Jan 18 '19 at 16:36
source share



All Articles