Unit testing MVC controller - need path to the application

1

I've inherited a legacy ASP.net MVC application and I'm trying to backfit unit test and running into an error. Here's my unit test:

public void DashboardController_Index_ReturnsIndex()
     {
         var dashboardController = _mocker.Resolve<DashboardController>();
         var controllerContextMock = new Mock<ControllerContext>();
         var userMock = new Mock<IUserContext>();

         userMock
             .SetupGet(u => u.Identity.IsAuthenticated)
             .Returns(true);
         userMock
             .Setup(u => u.Type)
             .Returns(EUserType.Normal);
         controllerContextMock
             .SetupGet(c => c.HttpContext.User)
             .Returns(userMock.Object);
         controllerContextMock
             .SetupGet(p => p.HttpContext.Request.Url)
             .Returns(new Uri("http://localhost/Dashboard/Index"));

         dashboardController.ControllerContext = controllerContextMock.Object;

         var result = dashboardController.Index() as ViewResult;

         Assert.Equal("Index", result.ViewName);
     }

When the Index action is called, it calls a static method in another class that returns a string expression used for configuration purposes. After that config string gets returned, another static method in another (referenced) static class (different namespace) gets called which attempts to parse that string expression but throws an exception. Here's that method:

private static string GetResourceString(HttpContextBase httpContext, string sxExpression, string sxVirtualPath)
        {
            var context = new ExpressionBuilderContext(sxVirtualPath);
            var builder = new ResourceExpressionBuilder();
            var fields = (ResourceExpressionFields)builder.ParseExpression(sxExpression, 
                typeof(string), context);

            if (string.IsNullOrEmpty(fields.ClassKey) == false)
                return (string)httpContext.GetGlobalResourceObject(fields.ClassKey, fields.ResourceKey, 
                    Thread.CurrentThread.CurrentCulture);
            return (string)httpContext.GetLocalResourceObject(sxVirtualPath, fields.ResourceKey, 
                Thread.CurrentThread.CurrentCulture);
        }

The exception occurs in the third line of this method where the code attempts to break the string into fields. Specifically, here's the exception:

System.Web.HttpException (0x80004005): The application relative virtual path '~/' cannot be made absolute, because the path to the application is not known.

I've seen this post that describes how to inject a fake into this call but since the Controller itself isn't trying to parse the string, but rather handing it off to a helper method in another class, it seems that DI won't work. Any idea how I can fake the application path while unit testing? In production, this code works just fine. I'm thinking I can refactor the controller to use this helper class as a dependency instead of just calling static methods but I'm hoping for a solution that doesn't require code changes to production code.

c#
asp.net-mvc
unit-testing
asked on Stack Overflow Feb 19, 2020 by Scott Reeves • edited Feb 19, 2020 by Scott Reeves

1 Answer

1

The proper way to do this, unfortunately, requires refactoring production code to allow for the helper method to be injected as a dependency. After all, isn't one of the advantages of creating unit tests to allow for refactoring code with confidence? In this case, the production code doesn't lend itself to back-fitting unit tests very easily (which is often the case when attempting to do such a thing.)

Best approach is to go up one abstraction level and create 'behavior tests' (as in Behavior Driven Development) that thoroughly check the behavior of the controller at a higher level, refactor it to allow for unit testing, and if the behavior hasn't changed, retro-fitting unit tests can be done with confidence.

answered on Stack Overflow Jan 16, 2021 by Scott Reeves

User contributions licensed under CC BY-SA 3.0