Testing the OnActionExecuting event of a Controller

28/12/2008

Unfortunately the images to this post were lost during the migration from Blogengine.Net to WordPress

I was doing a bit of refactoring on an ASP.NET MVC web application and was using the OnActionExecuting event in a controller to do authentication. I had not yet written a test for it, blasphemy I know, so that had to be corrected. The controller was something like this:

using System.Web.Mvc;

namespace TwotoContent.Controllers
{
    public class ExampleController : Controller
    {
        protected override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            // check if user is authenticated before executing an action and redirect if not
        }

        public ActionResult Action()
        {
            return View();
        }

        public ActionResult AnotherAction()
        {
            return View();
        }
    }
}

At first I did a stupid attempt thinking that if I would call a Controller action from the test, the OnActionExecuting would be automatically executed and tested. I quickly find out that’s not the case and after thinking it through it would probably suck if it worked like that because you would have to write the same setup code over and over again for every action for which the OnActionExecuting was called. But there must be the another way to call the OnActionExecuting function directly without having to execute an Action.

No luck, as you can see you can’t separately call the OnActionExecuting event on a controller because it’s protected in the Controller base class. I didn’t really know what to do so I googled around a bit and found this article about creating your own action filter. It seems that the OnActionExecuting event is public when you create your own custom ActionFilter. What is an ActionFilter you ask? Let me quote from the article: “An action filter consists of logic that runs directly before or directly after an action method runs. You can use action filters for logging, authentication, output caching, or other uses.” Exactly what I needed in other words. So after refactoring a bit I came up with the following code:

using System.Web.Mvc;

namespace TwotoContent.Controllers
{
    public class ExampleController : Controller
    {
        [ExampleActionFilter]
        public ActionResult Action()
        {
            return View();
        }

        [ExampleActionFilter]
        public ActionResult AnotherAction()
        {
            return View();
        }
    }

    public class ExampleActionFilterAttribute : ActionFilterAttribute
    {
        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            // check if user is authenticated before executing an action
        }
    }
}

Now the OnActionExecuting method in the ActionFilter attribute can now be tested as followed:

using System;
using System.Collections.Generic;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using NUnit.Framework;
using Rhino.Mocks;
using TwotoContent.Controllers;

namespace TwotoContentTests.FilterAttributes
{
    public class When_executing_an_event
    {
        private ActionExecutingContext _context;
        private ExampleController _controller;

        [SetUp]
        public void SetUp()
        {
            _controller = new ExampleController();
            _context = new ActionExecutingContext(new ControllerContext(MockRepository.GenerateMock<HttpContextBase>(),
                                                                        new RouteData(),
                                                                        _controller),
                                                  new Dictionary<string, object>());
        }

        [Test]
        public void Then_the_authenticated_indicator_must_be_set()
        {
            var subjectUnderTest = new ExampleActionFilterAttribute();

            subjectUnderTest.OnActionExecuting(_context);

            var authenticatedIndicator = Convert.ToBoolean(_context.Controller.ViewData["authenticated"]);

            Assert.That(authenticatedIndicator == true);
        }
    }
}

I first set up an ActionExecutingContext with a controller so it can be passed to the OnActionExecuting function. In the test I create an instance of the class that is under test, in this case the ExampleActionFilterAttribute class. As you can see the OnActionExecuting function can be called directly (because it’s public in the base class). After executing the function I read a variable from the ViewData collection via the context that was set up earlier. Finally I test if the variable in the ViewData is filled out correctly. You actually need quite some setup code to be able to mock an ActionExecutingContext. Because I didn’t need the HttpContextBase I have just injected a mock. This minimalizes the amount of Setup code that is needed.

I can’t really think of a reason why the OnActionExecuting event is protected on the base Controller class and is public on the ActionFilter class. A benefit of this approach is that it results in a smaller controller class and an ActionFilter we can reuse over various controller classes. So nothing but advantages! Maybe this isn’t the best solution but it worked for me. Ideas/additions/rants are always welcome.