How do I unit test a custom ActionFilter in ASP.Net MVC
Asked Answered
O

2

32

So I'm creating a custom ActionFilter that's based mostly on this project http://www.codeproject.com/KB/aspnet/aspnet_mvc_restapi.aspx.

I want a custom action filter that uses the http accept headers to return either JSON or Xml. A typical controller action will look like this:

[AcceptVerbs(HttpVerbs.Get)]
[AcceptTypesAttribute(HttpContentTypes.Json, HttpContentTypes.Xml)]
public ActionResult Index()
{
    var articles = Service.GetRecentArticles();

    return View(articles);
}

The custom filter overrides the OnActionExecuted and will serialize the object (in this example articles) as either JSON or Xml.

My question is: how do I test this?

  1. What tests do I write? I'm a TDD novice and am not 100% sure what I should be testing and what not to test. I came up with AcceptsTypeFilterJson_RequestHeaderAcceptsJson_ReturnsJson(), AcceptsTypeFilterXml_RequestHeaderAcceptsXml_ReturnsXml() and AcceptsTypeFilter_AcceptsHeaderMismatch_ReturnsError406().
  2. How do I test an ActionFilter in MVC that is testing the Http Accept Headers?

Thanks.

Offing answered 14/12, 2011 at 16:41 Comment(1)
Future readers may want to look at this answer : #36629891Elba
O
30

You just need to test the filter itself. Just create an instance and call the OnActionExecuted() method with test data then check the result. It helps to pull the code apart as much as possible. Most of the heavy lifting is done inside the CsvResult class which can be tested individually. You don't need to test the filter on an actual controller. Making that work is the MVC framework's responsibility.

public void AcceptsTypeFilterJson_RequestHeaderAcceptsJson_ReturnsJson()
{
    var context = new ActionExecutedContext();
    context.HttpContext = // mock an http context and set the accept-type. I don't know how to do this, but there are many questions about it.
    context.Result = new ViewResult(...); // What your controller would return
    var filter = new AcceptTypesAttribute(HttpContentTypes.Json);

    filter.OnActionExecuted(context);

    Assert.True(context.Result is JsonResult);
}
Outrank answered 14/12, 2011 at 17:12 Comment(1)
I don't know when it changed, but in .Net Core 3.1 ActionExecutedContext does not have a no-arg constructor, making setup more difficult.Herb
P
17

I just stumbled upon this blog post which seems the right way to me. He uses Moq.

What this chap is doing is mocking the HTTPContext, but also we need to set up a ContentType in the request:

// Mock out the context to run the action filter.
var request = new Mock<HttpRequestBase>();
request.SetupGet(r => r.ContentType).Returns("application/json");
    
var httpContext = new Mock<HttpContextBase>();
httpContext.SetupGet(c => c.Request).Returns(request.Object);
 
var routeData = new RouteData(); //
routeData.Values.Add("employeeId", "123");
 
var actionExecutedContext = new Mock<ActionExecutedContext>();
actionExecutedContext.SetupGet(r => r.RouteData).Returns(routeData);
actionExecutedContext.SetupGet(c => c.HttpContext).Returns(httpContext.Object);
 
var filter = new EmployeeGroupRestrictedActionFilterAttribute();
 
filter.OnActionExecuted(actionExecutedContext.Object);

Note - I have not tested this myself.

Pitiless answered 10/4, 2015 at 9:57 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.