Software Solutions for Ethical Businesses

How to unit test a MVC HTML Helper that calls Action

If you have a need to unit test a helper method which does a call to Action then you will hit an issue due to how HtmlHelper is not mockable and the call to Action will end up going through many layers and actually calling an action.

The below uses a simple property injection method to allow mocking the Action call so the rest of the helper functionality can be tested.

Helper class

Makes use of a static constructor and a public property holding the singleton of an invoker class that wraps the Action call.

    public static class SomeHtmlHelperClass
    {
        static SomeHtmlHelperClass()
        {
            ActionInvoker = new HtmlHelperActionInvoker();
        }

        public static IHtmlHelperActionInvoker ActionInvoker { get; set; }

        public static MvcHtmlString RenderCMSObject(this HtmlHelper helper, CMSObject cmsObject)
        {
            var actionName = string.IsNullOrEmpty(cmsObject.ActionName)
                ? "Index"
                : cmsObject.ActionName;

            var controllerName = string.IsNullOrEmpty(cmsObject.ControllerName)
                ? "Default"
                : cmsObject.ControllerName;

            return ActionInvoker.Action(helper, actionName, controllerName, cmsObject);
        }
    }

Invoker interface and class

Simple Interface and class that calls the Action method of helper, the concrete is very basic so it doesn't need testing.

    public interface IHtmlHelperActionInvoker
    {
        MvcHtmlString Action(HtmlHelper helper, string action, string controller, CMSObject model);
    }

    public class HtmlHelperActionInvoker : IHtmlHelperActionInvoker
    {
        public MvcHtmlString Action(HtmlHelper helper, string action, string controller, CMSObject model)
        {
            return helper.Action(action, controller, model);
        }
    }

Unit Test

Mocks the invoker using Moq to return a string, which is then asserted at the end. The mocked class is injected into the helper via property injection before the helper is executed.

    [TestFixture]
    public class HelperTests
    {
        [Test]
        public void GivenACmsObjectWithCompletedActionAndController_WhenRenderCMSObject_ThenExpectedActionOutcomeforActionAndControllerIsGiven()
        {
            const string actionName = "foo";
            const string controllerName = "bar";
            const string expectedOutcome = "<h1>Bruce</h1>";

            // Arrange
            var cmsObject = new CMSObject { ActionName = actionName, ControllerName = controllerName };

            var mockInvoker = new Mock<IHtmlHelperActionInvoker>();
            mockInvoker.Setup(x => x.Action(null, actionName, controllerName, cmsObject)).Returns(MvcHtmlString.Create(expectedOutcome));
            SomeHtmlHelperClass.ActionInvoker = mockInvoker.Object;

            // Act
            var result = SomeHtmlHelperClass.RenderCMSObject(null, cmsObject);

            // Verify
            Assert.That(result.ToString(), Is.EqualTo(expectedOutcome));
        }
    }

Get in Touch

Please fix the highlighted fields.

Thank you, your message has been sent.