Using and Testing HTML Helpers in MVC

If you’ve done any amount of development with MVC you are probably aware that it’s pretty easy to end up with a lot of clutter in your views.  Today I’m going to illustrate how we can use custom helpers to neaten views and also show how we can write unit tests for those helpers.

Example

During my December holiday I went to the planetarium in Cape Town so I thought as an example I would write a simple website that shows the planets in our solar system and allows you to select one and see more information on that planet.

Example

Sorry, Pluto.  Now let’s say when a planet is selected we don’t want that planet to be a link.

Selected

Easy right?  Let’s take a look at the view.

Cluttered View

<ul>
<% foreach(var planet in Model) { %>
    <li>
    <% if (selected != null && selected.Id == planet.Id) { %>
        <span><%:planet.Name%></span>
    <% } else { %>
        <%=Html.ActionLink(planet.Name, "Index", new { planet.Id })%>
    <% } %>
    </li>
<% } %>
</ul>

This is obviously a rather simple example, but even here our view seems to contain too much code.  One way of removing the amount of code in the view is to write a custom helper.  I’m going to write a helper that renders a span for the selected item and a link otherwise.

Custom Helper

public static class HtmlHelpers
{
    public static MvcHtmlString SelectedLink(this HtmlHelper helper, bool selected, string text, string action, object routeValues)
    {
        if (selected)
        {
            return MvcHtmlString.Create(string.Format("<span>{0}</span>", text));
        }
        else
        {
            return helper.ActionLink(text, action, routeValues);
        }
    }
}

Now the code in our view becomes much simpler.

<ul>
<% foreach(var planet in Model) { %>
    <li><%=Html.SelectedLink(selectedId == planet.Id, planet.Name, "Index", new { planet.Id })%></li>
<% } %>
</ul>

It’s not a massive change, but as we add more and more code to our views it makes them difficult to test and increasingly difficult to share code between views.  Custom helpers are an elegant solution to this problem.

Test the Custom Helpers

It gets even better – we can unit test our custom helpers.  I prefer to use TDD when writing code and custom helpers are no exception.

[TestMethod]
public void ShouldReturnActionLinkWhenNotSelected()
{
    var result = helper.SelectedLink(false, "description", "action", null).ToString();

    StringAssert.StartsWith(result, "<a");
    StringAssert.EndsWith(result, "</a>");
}

[TestMethod]
public void ShouldRenderSpanWhenSelected()
{
    var result = helper.SelectedLink(true, "description", "action", null).ToString();

    Assert.AreEqual("<span>description</span>", result);
}

This allows us unit test our views and all that remains untested in our views is markup.

Happy coding.

Planets2008