Unit Testing Made Easy with Custom Model Binders

In ASP.NET MVC 3, Microsoft has made unit testing controller actions quite easy. Generally you follow 3 basic steps:

  • setup your dependencies
  • call the controller action you want to test
  • assert something on the properties(usually the Model property) of the returned ActionResult derived object.

However there still is a pain point when the action under test makes use of so called ASP.NET intrinsic objects (Request, Response, Session, Cache, Server etc.) which are hold in the HttpContext instance.

Mocking the HttpContext, while doable, is not quite straight forward, nor as fast as mocking a simpler object. It simply is an ugly job.

But do not panic! There is a solution which in my opinion is the most elegant of all: Custom Model Binders. They can be use to factor out of the action methods those hard  to mock (relatively speaking) intrinsic objects.

Lets look at an example. Suppose I have a custom made identity object that implements some very “special” logic.

    public class MyIdentity : IIdentity
    {
        public string MyOwnCustomIdentiyPropertyThatSupposedlyINeedSomewhere { get; set; }

        public string AuthenticationType
        {
            get { return "Custom"; }
        }

        public bool IsAuthenticated
        {
            get { return true; }
        }

        public string Name
        {
            get { return "Name"; }
        }
    }
    

The following is the controller action that uses this Identity object:

        public ActionResult About()
        {
            ViewBag.Message = (ControllerContext.HttpContext.User.Identity as MyIdentity).MyOwnCustomIdentiyPropertyThatSupposedlyINeedSomewhere;

            return View();
        }
         

Testing this action method will be impossible unless a mock for HttpContext is supplied which we try to avoid.

Wouldn’t it be nice to have an action method like the following?

        public ActionResult Index(MyIdentity identity)
        {
            ViewBag.Message = identity.MyOwnCustomIdentiyPropertyThatSupposedlyINeedSomewhere;

            return View();
        }
        

Well it would be very nice and we can. This is possible by the help of a custom model binder for the MyIdentity type that will be registered on application startup. Code shown bellow:

    public class MyIdentityModelBinder : IModelBinder
    {
        public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
        {
            return controllerContext.HttpContext.User.Identity;
        }
    }

        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();

            //register the new binder
            ModelBinders.Binders.Add(typeof(MyIdentity), new MyIdentityModelBinder());

        }

Now I can write a simple unit test for this functionality like this:

        [TestMethod]
        public void Index_ShoudlDisplayCorrectMessageFromIdentity()
        {
            //arrange
            var dummyIdentity = new MyIdentity();
            dummyIdentity.MyOwnCustomIdentiyPropertyThatSupposedlyINeedSomewhere = "test";
            var sut = new HomeController();

            //act
            ViewResult result = sut.Index(dummyIdentity) as ViewResult;

            //assert
            Assert.AreEqual(result.ViewBag.Message, "test");
        }

The same trick can be applied for any of the intrinsic objects or their properties. You just need to create other custom binders.

And this is it, hope this will make your life easier when creating tests for the controller.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s