views:

246

answers:

1

As suggested by (among others) Kazi Manzur Rashid in this blog post, I am using ActionFilterAttributes to transfer model state from one request to another when redirecting.

However, I find myself unable to write a unit test that test the behavior of these attributes. As an example, this what I want the test for the ImportModelStateAttribute to do:

  1. Setup the filterContext so that TempData[myKey] contains some fake "exported" ModelState (that is, a ModelStateDictionary I create myself, and add one error to)
  2. Make ModelState contain one model error.
  3. Call OnActionExecuting.
  4. Verify the two dictionaries are merged, and ModelState now contains both errors.

I'm at a loss already on the first step.

EDIT:
Yes, I've tried mocking ActionFilterAttribute with Moq, but I get errors stating

Invalid setup on non-overridable member

for both TempData and ModelState.

+1  A: 

Tomas, You do not have to mock the filterContext, you can create the real object for testing the action filter, the same goes for the model state, these are poco objects. Only thing that you have to mock is the HttpContext (if needed).

[Fact]
public void Should_import_complete_view_data()
{
    var attribute = new ImportViewDataFromTempDataAttribute();

    var httpContext = new Mock<HttpContextBase>();
    var requestContext = new RequestContext(httpContext.Object, new RouteData());

    var previousModel = new object();
    var previousViewData = new ViewDataDictionary(previousModel) {{"foo", "bar"}};

    previousViewData.ModelState.AddModelError("foo", "bar");

    var controller = new Mock<ControllerBase>();
    controller.Object.ViewData = new ViewDataDictionary();
    controller.Object.TempData = new TempDataDictionary { { attribute.Key, previousViewData } };

    var controllerContext = new ControllerContext(requestContext, controller.Object);
    var actionContext = new ActionExecutingContext(controllerContext, new Mock<ActionDescriptor>().Object, new Dictionary<string, object>());

    attribute.OnActionExecuting(actionContext);

    Assert.True(actionContext.Controller.ViewData.ContainsKey("foo"));
    Assert.True(actionContext.Controller.ViewData.ModelState.ContainsKey("foo"));
    Assert.Same(previousModel, actionContext.Controller.ViewData.Model);
}
kazimanzurrashid
Haha, sometimes we tend to make things harder than they are, don't we? =) My solution: I created a `FakeController` class that inherits `Controller` and added the model state and temp data I wanted. I then mocked the filter contexts and set up the mocks to return my instance of `FakeController`. Works like a charm! Thanks! =)
Tomas Lycken