views:

277

answers:

1

In a custom ActionFilter, I want check the attributes on the controller action that will be executed. Running through a small test application, the following works when launching the app in the asp.net development server-

public class CustomActionFilterAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        var someAttribute = filterContext.ActionDescriptor
                                    .GetCustomAttributes(typeof(SomeAttribute), false)
                                    .Cast<SomeAttribute>()
                                    .SingleOrDefault();

        if (someAttribute == null)
        {
            throw new ArgumentException();
        }

        // do something here
    }

    public override void OnActionExecuted(ActionExecutingContext filterContext)
    {
        // ...
    }
}

An action method without SomeAttribute throws an ArgumentException and conversely, an action method with SomeAttribute does not. So far so good.

Now I would like to set up some unit tests for the ActionFilter, but how can I set up the action method upon which the OnActionExecuting method should run in the unit test? Using the following code doesn't find SomeAttribute on the action method which will be executed. Is the test set up correctly? Have I not arranged something correctly in the test? To clarify, the test is not complete but I'm not sure what I've missed such that someAttribute in OnActionExecuting in the test is null

    [TestMethod]
    public void Controller_With_SomeAttribute()
    {
        FakeController fakeController =
            new FakeController();

        ControllerContext controllerContext = 
            new ControllerContext(new Mock<HttpContextBase>().Object, 
                                  new RouteData(), 
                                  fakeController);

        var actionDescriptor = new Mock<ActionDescriptor>();
        actionDescriptor.SetupGet(x => x.ActionName).Returns("Action_With_SomeAttribute");

        ActionExecutingContext actionExecutingContext =
            new ActionExecutingContext(controllerContext,
                                       actionDescriptor.Object, 
                                       new RouteValueDictionary());

        CustomActionFilterAttribute customActionFilterAttribute = new CustomActionFilterAttribute ();
        customActionFilterAttribute.OnActionExecuting(actionExecutingContext);
    }

    private class FakeController : Controller
    {
        [SomeAttribute]
        ActionResult Action_With_SomeAttribute()
        {
            return View();
        }
    }
A: 

Since the ActionDescriptor property of ActionExecutingContext is virtual, you can just override that and provide your own implementation of ActionDescriptor.

Here are two tests that verify the two branches through the current implementation of OnActionExecuting:

[ExpectedException(typeof(ArgumentException))]
[TestMethod]
public void OnActionExecutingWillThrowWhenSomeAttributeIsNotPresent()
{
    // Fixture setup
    var ctxStub = new Mock<ActionExecutingContext>();
    ctxStub.Setup(ctx => ctx.ActionDescriptor.GetCustomAttributes(typeof(SomeAttribute), false))
        .Returns(new object[0]);

    var sut = new CustomActionFilterAttribute();
    // Exercise system
    sut.OnActionExecuting(ctxStub.Object);
    // Verify outcome (expected exception)
    // Teardown
}

[TestMethod]
public void OnActionExecutingWillNotThrowWhenSomeAttributeIsPresent()
{
    // Fixture setup
    var ctxStub = new Mock<ActionExecutingContext>();
    ctxStub.Setup(ctx => ctx.ActionDescriptor.GetCustomAttributes(typeof(SomeAttribute), false))
        .Returns(new object[] { new SomeAttribute() });

    var sut = new CustomActionFilterAttribute();
    // Exercise system
    sut.OnActionExecuting(ctxStub.Object);
    // Verify outcome (no exception indicates success)
    // Teardown
}
Mark Seemann