DL Narasimhan
DL Narasimhan

Reputation: 733

How can I write unit test to test a ASP.NET MVC controller with the following code snippet

All of these views don't even have a title. So I cannot do an

Assert.AreEqual("Error",Viewbag.Title);

How else can I test an error controller to ensure atleast 85% code is covered

public class ErrorController : Controller
{
    public ActionResult Index()
    {
        return View();
    }

    public ActionResult NotFound()
    {
        return View();
    }
    public ActionResult BadRequest()
    {
        return View();
    }

    public ActionResult ServerError()
    {
        return View();
    }
}

The best I could come up with was

public class ErrorControllerTests : BaseTestController
{
    ErrorController ec = new ErrorController();
    [TestMethod()]
    public void IndexTest()
    {
        var actionResult = ec.Index() as ViewResult;

        Assert.AreSame(ec.ViewData, actionResult.ViewData);
        Assert.AreSame(ec.TempData, actionResult.TempData);

        Assert.AreEqual(actionResult.ViewName,"");
    }

    [TestMethod()]
    public void NotFoundTest()
    {
        var NotFoundTestResult = ec.NotFound() as ViewResult;
        Assert.AreEqual(NotFoundTestResult.ViewName,"");
    }

    [TestMethod()]
    public void BadRequestTest()
    {
        var badRequestTestResult = ec.BadRequest() as ViewResult;
        Assert.AreEqual(badRequestTestResult.ViewName,"");
    }

    [TestMethod()]
    public void ServerErrorTest()
    {
        var serverErrorTestResult = ec.ServerError() as ViewResult;
        Assert.AreEqual(serverErrorTestResult.ViewName, "");
    }
}

Upvotes: 2

Views: 1588

Answers (2)

Nkosi
Nkosi

Reputation: 247153

You basically trying to test the Framework which should have already been done by Microsoft who owns the framework. You should try to avoid testing code you don't own or control.

That said, here is a work around to test the ErrorController that tries to mimic framework functionality and work around the limitations when it comes to unit tests.

[TestClass]
public class ErrorControllerTests {
    [TestMethod]
    public void TestIndexView() {
        //Arrange
        var expectedViewName = GetViewNameFromExpression<ErrorController>(c => c.Index());
        var controller = ArrangeErrorController(expectedViewName);

        //Act
        var result = controller.Index() as ViewResult;

        //Assert
        Assert.IsNotNull(result);

        //Replicating Framework functionality
        MockExecuteResult(controller, result);

        Assert.AreEqual(expectedViewName, result.ViewName);
        CollectionAssert.AreEquivalent(controller.ViewData.ToList(), result.ViewData.ToList());
        CollectionAssert.AreEquivalent(controller.TempData.ToList(), result.TempData.ToList());
    }

    private static void MockExecuteResult(ErrorController controller, ViewResult result) {
        try {
            result.View = Mock.Of<IView>();
            result.ExecuteResult(controller.ControllerContext);
        } catch { }
    }

    private static ErrorController ArrangeErrorController(string actionName) {
        var controllerName = "error";
        var context = GetControllerContext(actionName, controllerName);
        var controller = new ErrorController() {
            ControllerContext = context
        };
        context.Controller = controller;
        return controller;
    }

    private static ControllerContext GetControllerContext(string actionName, string controllerName) {
        RouteData rd = new RouteData();
        rd.Values["action"] = actionName;
        rd.Values["controller"] = controllerName;

        var mockHttpContext = new Mock<HttpContextBase>();
        mockHttpContext.Setup(c => c.Session).Returns((HttpSessionStateBase)null);
        mockHttpContext.Setup(_ => _.Response.Output).Returns(new StringWriter());

        return new ControllerContext(mockHttpContext.Object, rd, new Mock<Controller>().Object);
    }

    private static string GetViewNameFromExpression<TController>(Expression<Action<TController>> action) {
        if (action == null) {
            throw new ArgumentNullException("action");
        }

        var type = typeof(TController);

        bool isController = type != null
               && type.Name.EndsWith("Controller", StringComparison.OrdinalIgnoreCase)
               && !type.IsAbstract
               && typeof(IController).IsAssignableFrom(type);

        if (!isController) {
            throw new InvalidOperationException("Invalid controller.");
        }

        MethodCallExpression body = action.Body as MethodCallExpression;
        if (body == null)
            throw new InvalidOperationException("Expression must be a method call.");

        if (body.Object != action.Parameters[0])
            throw new InvalidOperationException("Method call must target lambda argument.");

        string actionName = body.Method.Name;
        return actionName;
    }

}

Upvotes: 2

Misha Huziuk
Misha Huziuk

Reputation: 170

You can test for example view name:

Assert.AreEqual("Index", result.ViewName);

But to do that, you need to specify view name in action return:

public class ErrorController : Controller 
{
        public ActionResult Index()
        {
            return View("Index");
        }

        public ActionResult NotFound()
        {
            return View("NotFound");
        }
        public ActionResult BadRequest()
        {
            return View("BadRequest");
        }

        public ActionResult ServerError()
        {
            return View("ServerError");
        }
    }

Upvotes: 1

Related Questions