Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unit Testing Action Filters - How to mock ViewResult

I did a search on SO and looks like this question gets asked quite often. I have been able to get the mocks working and I'm also able to execute OnActionExecuted() without any issues. Here's my Unit Test. The commented lines are the ones that fail and I'm sure I'm not mocking the right type.

        //Arrange
        //var viewResult = new ViewResult();
        var filterContextMock = new Mock<ActionExecutedContext>();
        var routeData = new RouteData();
        var httpContextMock = new Mock<HttpContextBase>();

        routeData.Values["data"] = "Mock data";
        var requestContext = new RequestContext(httpContextMock.Object, routeData);

        var controller = new FakeController();
        controller.ControllerContext = new ControllerContext(requestContext, controller);

        filterContextMock.Setup(f => f.RouteData).Returns(routeData);
        filterContextMock.Setup(f => f.Controller).Returns(controller);
        //filterContextMock.Setup(f => f.Result).Returns(viewResult);

        //Act
        var wrapFilterAttribute = new WrapFilterAttribute();
        wrapFilterAttribute.OnActionExecuted(filterContextMock.Object);

Here is my Action Filter.

public class WrapFilterAttribute : ActionFilterAttribute
{
    public override void OnActionExecuted(ActionExecutedContext filterContext)
    {
        var view = (ViewResultBase)filterContext.Result;

        if (view != null)
        {
            BaseViewModel viewModel = (BaseViewModel)view.ViewData.Model ?? new BaseViewModel();
            viewModel.Wrap = new WrapperFactory().GetWrap();
        }

        base.OnActionExecuted(filterContext);
    }
}

The issue I'm facing here is filterContext.Result always comes in as EmptyResult. I'd like to push in a hydrated ViewResult instead. Any ideas how I can accomplish this?

Many thanks!

like image 695
Praveen Avatar asked Oct 01 '10 15:10

Praveen


1 Answers

First let's start by fixing your action filter as currently the code looks bad and those castings might bring you headaches:

public class WrapFilterAttribute : ActionFilterAttribute
{
    public override void OnActionExecuted(ActionExecutedContext filterContext)
    {
        var view = filterContext.Result as ViewResultBase;
        if (view != null)
        {
            // the controller action returned a ViewResultBase
            var viewModel = view.ViewData.Model as BaseViewModel;
            if (viewModel != null)
            {
                // the model passed to the view was derived from
                // BaseViewModel so we can safely update the Wrap
                // property
                viewModel.Wrap = new WrapperFactory().GetWrap();
            }
        }
        base.OnActionExecuted(filterContext);
    }
}

And the unit test:

// arrange
var sut = new WrapFilterAttribute();
var filterContextMock = new Mock<ActionExecutedContext>();
var viewResultMock = new Mock<ViewResultBase>();
filterContextMock.Object.Result = viewResultMock.Object;
var viewModel = new BaseViewModel();
viewResultMock.Object.ViewData.Model = viewModel;

// act
sut.OnActionExecuted(filterContextMock.Object);

// assert
// TODO: assert something on the viewModel.Wrap property like 
// for example that it has been initialized

Remark: Your action filter has a strong dependency on WrapperFactory class. This is not good. A further improvement would be to abstract this functionality into an interface which would be injected into the constructor of the action filter. This would allow you further separation of concerns between different layers of your application.

like image 51
Darin Dimitrov Avatar answered Nov 12 '22 16:11

Darin Dimitrov