How to mock ActionExecutingContext with Moq?

yokomizor picture yokomizor · Apr 14, 2016 · Viewed 8.9k times · Source

I am trying to test the following filter:

using Microsoft.AspNet.Mvc;
using Microsoft.AspNet.Mvc.Filters;

namespace Hello
{
    public class ValidationFilter : ActionFilterAttribute
    {
        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            if (!filterContext.ModelState.IsValid) {
                filterContext.Result = new BadRequestObjectResult(filterContext.ModelState);
            }
        }
    }
}

I am trying to mock the ActionFilterAttribute using Moq.

I am using Mvc 6.0.0-rc1

My first try:

var context = new ActionExecutingContext(
                    new ActionContext(Mock.Of<HttpContext>(), new RouteData(), new ActionDescriptor()),
                    new IFilterMetadata[] { filter, },
                    new Dictionary<string, object>(),
                    controller: new object());

But I could not override the ModelState. It looks to be readonly: https://github.com/aspnet/Mvc/blob/6.0.0-rc1/src/Microsoft.AspNet.Mvc.Abstractions/ActionContext.cs#L25

Then I tried:

var contextMock = new Mock<ActionExecutingContext>(
                    new ActionContext(Mock.Of<HttpContext>(), new RouteData(), new ActionDescriptor()),
                    new IFilterMetadata[] { filter, },
                    new Dictionary<string, object>());

But when I call filter.OnActionExecuting(contextMock.Object) I get the following error:

Castle.DynamicProxy.InvalidProxyConstructorArgumentsException : Can not instantiate proxy of class: Microsoft.AspNet.Mvc.Filters.ActionExecutingContext.
      Could not find a constructor that would match given arguments:
      Microsoft.AspNet.Mvc.ActionContext
      Microsoft.AspNet.Mvc.Filters.IFilterMetadata[]
      System.Collections.Generic.Dictionary`2[System.String,System.Object]

How to test a filter that uses ModelState?

Answer

ChristofferH picture ChristofferH · Sep 14, 2016

I just stumbled on the same problem and solved it in this way.

[Fact]
public void ValidateModelAttributes_SetsResultToBadRequest_IfModelIsInvalid()
{
    var validationFilter = new ValidationFilter();
    var modelState = new ModelStateDictionary();
    modelState.AddModelError("name", "invalid");

    var actionContext = new ActionContext(
        Mock.Of<HttpContext>(),
        Mock.Of<RouteData>(),
        Mock.Of<ActionDescriptor>(),
        modelState
    );

    var actionExecutingContext = new ActionExecutingContext(
        actionContext,
        new List<IFilterMetadata>(),
        new Dictionary<string, object>(),
        Mock.Of<Controller>()
    );

    validationFilter.OnActionExecuting(actionExecutingContext);

    Assert.IsType<BadRequestObjectResult>(actionExecutingContext.Result);
}