Skip to content

Unit Testing Html Helpers for ASP.NET MVC

Html helpers for ASP.NET MVC are static extension methods, which frequently reference the ViewContext and  HttpContext. Combined, this can make unit testing a bit tricky. Let’s write a new Html helper using a test-first methodology. Let’s start with a prototype function:

        public static MvcHtmlString MyTable(this HtmlHelper helper, MyModel model, IDictionary<stringobject> htmlAttributes)
        {
            return MvcHtmlString.Empty;
        }

I’ve added just enough code here to get the prototype to compile. Now let’s write some unit tests. First we need to validate the arguments. I’m using MSTest here:

        [TestMethod]
        [ExpectedException(typeof(ArgumentNullException))]
        public void MyTable_should_throw_when_helper_argument_null()
        {
            var model = new MyModel();

            MyHelpers.MyTable(null, model, null);
        }

        [TestMethod]
        [ExpectedException(typeof(ArgumentNullException))]
        public void MyTable_should_throw_when_model_argument_null()
        {
            var hh = new HtmlHelper(null, null);

            MyHelpers.MyTable(hh, null, null);
        }

Note that I have elected to call this method as a regular static method rather than as an extension method. Either one is valid, but since I am unit testing the case where the HtmlHelper argument is null, it makes more sense to call the method under test as a regular static method.

Those tests will fail when run, so I’ll write some code to fix that:

        public static MvcHtmlString MyTable(this HtmlHelper helper, MyModel model, IDictionary<stringobject> htmlAttributes)
        {
            if (helper == null)
            {
                throw new ArgumentNullException("helper");
            }
            if (model == null)
            {
                throw new ArgumentNullException("model");
            }
            return MvcHtmlString.Empty;
        }

Now let’s presume that the requirements of the new method specify that the table should get its HTML id from the HttpContext.Items. This is of course a very stupid place to store an HTML id, but I want to demonstrate how to fake the HttpContext, since that is something that people often find difficult. So let’s make a fake HttpContext. I’ll need a lightweight implementation of IViewDataContainer, as well, for reasons which will be clear later on:

        private class FakeHttpContext : HttpContextBase
        {
            private Dictionary<object, object> _items = new Dictionary<object, object>();
            public override IDictionary Items { get { return _items; } }
        }

        private class FakeViewDataContainer : IViewDataContainer
        {
            private ViewDataDictionary _viewData = new ViewDataDictionary();
            public ViewDataDictionary ViewData { get { return _viewData; } set { _viewData = value; } }
        }

I elected to only implement the Items property of HttpContextBase, since that is the only one I’ll need to reference in the implementation of my helper. Were I to reference other HttpContextBase properties which I did not override, I would expect to see a NotImplementedException at runtime.

With these two, small classes, I can write a test for the helper which will execute without throwing any exceptions. Well, except for the fact that the assertion fails:

        [TestMethod]
        public void MyTable_should_render_HTML_table_with_id_from_http_context()
        {
            var vc = new ViewContext();
            vc.HttpContext = new FakeHttpContext();
            vc.HttpContext.Items.Add(MyHelpers.IdKey, "foo");
            var hh = new HtmlHelper(vc, new FakeViewDataContainer());
            var model = new MyModel();

            var result = MyHelpers.MyTable(hh, model, null).ToString();

            Assert.AreEqual("<table id = \"foo\"></table>", result);
        }

I needed the IViewDataContainer because the two argument constructor for HtmlHelper will throw ArgumentNullException if you pass a null for the second argument.

Finally, I can implement the helper to make this unit test pass:

        public static MvcHtmlString MyTable(this HtmlHelper helper, MyModel model, IDictionary<stringobject> htmlAttributes)
        {
            if (helper == null)
            {
                throw new ArgumentNullException("helper");
            }
            if (model == null)
            {
                throw new ArgumentNullException("model");
            }
            TagBuilder tagBuilder = new TagBuilder("table");
            tagBuilder.MergeAttributes(htmlAttributes);
            tagBuilder.GenerateId(helper.ViewContext.HttpContext.Items[MyHelpers.IdKey]);
            return MvcHtmlString.Create(tagBuilder.ToString(TagRenderMode.Normal));
        }

{ 3 } Comments

  1. Joe Raben | September 21, 2010 at 4:36 pm | Permalink

    Can you program a puzzle like the one at acrostics.org ? I need such a program and am seeking a programmer who will work with/for me.

  2. alexanderb | October 30, 2010 at 9:12 am | Permalink

    Thanks! This is exactly what I needed, it was not really clear to me with unit testing of extension.. namely faking IViewDataContainer.

  3. nomel | June 23, 2012 at 8:20 am | Permalink

    I think that it is a virtuous endeavor to vindicate a difficult subject.

{ 1 } Trackback

  1. [...] http://blogs.teamb.com/craigstuntz/2010/09/10/38638 [...]

Post a Comment

Your email is never published nor shared. Required fields are marked *

Bad Behavior has blocked 713 access attempts in the last 7 days.

Close