Search code examples
asp.net-mvcunit-testingcontrolleraction-filter

Unit testing all controllers from a single test


I just created an action filter that I want to apply to nearly all of my controllers (including any new ones that are introduced later on).

I figure a really useful unit test would be one that cycles through each controller and verifies that if certain criteria are met, then the action filter will impact the result.

Is it wise to create a unit test that hits multiple controllers? Can anyone share code from a similar test that has proven useful?

EDIT: Just realized that testing an action filter might be problematic. Still, if you have thoughts to share on mass testing of controllers...


Solution

  • It is not recommended to test more than one thing at a time in tests.

    You should also avoid logic in tests (switch, if, else, foreach, for, while) as the test is less readable and possibly introduces hidden bugs.

    Many simple, readable, and therefore maintainable tests that are only testing one thing each are far preferable to one test with a lot of complexity.

    RESPONSE TO YOUR EDIT

    Testing filters can be achieved by separating the filter from the attribute. Here is an example: The LoadMembershipTypeListFilter class has the 'seams' needed to use test fakes. This is where your logic in your filter is that is to be tested.

    public class LoadMembershipTypeListFilter : IActionFilter
    {
        private IMembershipTypeProvider _provider;
        private IMembershipTypeAdminMapper _mapper;
    
        public LoadMembershipTypeListFilter(IMembershipTypeProvider provider, IMembershipTypeAdminMapper mapper)
        {
            _provider = provider;
            _mapper = mapper;
        }
    
        #region IActionFilter Members
        public void OnActionExecuted(ActionExecutedContext filterContext)
        {
        }
    
        public void OnActionExecuting(ActionExecutingContext filterContext)
        {
            //implementation...
        }
        #endregion
    }
    

    And the attribute here uses the filter, this example resolves the dependencies the filter requires by a call to the service locator:

    public class LoadMembershipTypeListAttribute : ActionFilterAttribute
    {
        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            var filter = new LoadMembershipTypeListFilter(IocResolve.Resolve<IMembershipTypeProvider>(), IocResolve.Resolve<IMembershipTypeAdminMapper>());
            filter.OnActionExecuting(filterContext);
        }
    }
    

    And your controller uses the attribute:

    [LoadMembershipTypeList]
    public ActionResult Create()
    {
        return View();
    }