How to unit test a MVC HTML Helper that calls Action
Posted on November 17, 2013 • 2 minutes • 311 words
Table of contents
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));
}
}