c# - 如果我在测试时在 System.Web.UI.Util 处遇到异常,如何对我的 HtmlHelper 扩展方法进行单元测试?

标签 c# asp.net-mvc-3 unit-testing moq html-helper

嗯...问题更复杂,然后问题标题说。首先,我有一个 HtmlHelper 的扩展方法,它根据当前的路由参数生成带参数的 html 链接。也就是说,如果我在页面 .../page?param1=val1&param2=val2 ,当我调用我的方法时 ActionQuryLink生成链接,例如 @Html.ActionQuryLink("link text", "action", new { param3 = "value3" })我会得到一个链接到 <a href=".../page?param1=val1&param2=val2&param3=value3">link text</a> .嗯,扩展类本身是:

public static class ActionLinkHelper
    {
        public static MvcHtmlString ActionQueryLink(this HtmlHelper htmlHelper, string linkText, string action)
        {
            return (ActionQueryLink(htmlHelper, linkText, action, null, null));
        }
        public static MvcHtmlString ActionQueryLink(this HtmlHelper htmlHelper, string linkText, string action, object routeValues)
        {

            /*line 16*/return (ActionQueryLink(htmlHelper, linkText, action, routeValues, null));
        }

        public static MvcHtmlString ActionQueryLink(this HtmlHelper htmlHelper, string linkText, string action, object routeValues, IDictionary<string, object> htmlAttributes)
        {
            var queryString = htmlHelper.ViewContext.HttpContext.Request.QueryString;

            var newRoute = routeValues == null
                ? htmlHelper.ViewContext.RouteData.Values
                : new RouteValueDictionary(routeValues);

            foreach(string key in queryString.Keys)
            {
                if(!newRoute.ContainsKey(key))
                    newRoute.Add(key, queryString[key]);
            }
            /*line 32*/string generatedLink = HtmlHelper.GenerateLink(
                htmlHelper.ViewContext.RequestContext,
                htmlHelper.RouteCollection,
                linkText,
                null,
                action,
                null,
                newRoute,
                htmlAttributes);

            return new MvcHtmlString(generatedLink);
        }
    }

主要问题是测试这个扩展方法

我的单元测试看起来像:

[TestClass]
    public class ActionLinkHeplerTests
    {
        #region ActionQueryLink
        [TestMethod]
        public void ActionLinkHeplerShouldGenerateCorrectActionLink()
        {
            var mockHttpContext = new Mock<HttpContextBase>();
            mockHttpContext.Setup(c => c.Request.QueryString).Returns(new NameValueCollection { { "param1", "value1" } });
            mockHttpContext.Setup(c => c.Request.AppRelativeCurrentExecutionFilePath).Returns("~/");
            mockHttpContext.Setup(c => c.Request.ApplicationPath).Returns("~/");
            mockHttpContext.Setup(c => c.Request.CurrentExecutionFilePath).Returns("~/");

            var mockProductRepository = new Mock<IProductRepository>();
            mockProductRepository.Setup(p => p.GetCategory(It.IsAny<string>())).Returns(new Category());
            var mockSettings = new Mock<ISettings>();
            var categoryController = new CategoryController(mockProductRepository.Object, mockSettings.Object);

            var mockViewDataContainer = new Mock<IViewDataContainer>();
            mockViewDataContainer.Setup(e => e.ViewData).Returns(new ViewDataDictionary { { "action", "action" } });

            var viewContext = new ViewContext
                                  {
                                      HttpContext = categoryController.HttpContext,
                                      RequestContext = new RequestContext
                                                           {
                                                               HttpContext = mockHttpContext.Object,
                                                               RouteData = new RouteData()
                                                           }
                                  };

            var mockRouteHandler = new Mock<IRouteHandler>();
            var helper = new HtmlHelper(viewContext, mockViewDataContainer.Object, new RouteCollection { { "action", new Route("controller/action", mockRouteHandler.Object) } });

            var expected = new MvcHtmlString("");
            /*line 51*/var actual = helper.ActionQueryLink("link text", "action", new {view = "list"});

            Assert.AreEqual(expected, actual);
        }
        #endregion
    }

我得到这样的异常:

Test method TestSite.UnitTests.Helpers.ActionLinkHeplerTests.ActionLinkHeplerShouldGenerateCorrectActionLink threw exception: 
System.NullReferenceException: Object reference not set to an instance of an object.

和堆栈跟踪:

at System.Web.UI.Util.GetUrlWithApplicationPath(HttpContextBase context, String url)
   at System.Web.Routing.RouteCollection.NormalizeVirtualPath(RequestContext requestContext, String virtualPath)
   at System.Web.Routing.RouteCollection.GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
   at System.Web.Mvc.RouteCollectionExtensions.GetVirtualPathForArea(RouteCollection routes, RequestContext requestContext, String name, RouteValueDictionary values, ref Boolean usingAreas)
   at System.Web.Mvc.UrlHelper.GenerateUrl(String routeName, String actionName, String controllerName, RouteValueDictionary routeValues, RouteCollection routeCollection, RequestContext requestContext, Boolean includeImplicitMvcValues)
   at System.Web.Mvc.UrlHelper.GenerateUrl(String routeName, String actionName, String controllerName, String protocol, String hostName, String fragment, RouteValueDictionary routeValues, RouteCollection routeCollection, RequestContext requestContext, Boolean includeImplicitMvcValues)
   at System.Web.Mvc.HtmlHelper.GenerateLinkInternal(RequestContext requestContext, RouteCollection routeCollection, String linkText, String routeName, String actionName, String controllerName, String protocol, String hostName, String fragment, RouteValueDictionary routeValues, IDictionary`2 htmlAttributes, Boolean includeImplicitMvcValues)
   at System.Web.Mvc.HtmlHelper.GenerateLink(RequestContext requestContext, RouteCollection routeCollection, String linkText, String routeName, String actionName, String controllerName, String protocol, String hostName, String fragment, RouteValueDictionary routeValues, IDictionary`2 htmlAttributes)
   at System.Web.Mvc.HtmlHelper.GenerateLink(RequestContext requestContext, RouteCollection routeCollection, String linkText, String routeName, String actionName, String controllerName, RouteValueDictionary routeValues, IDictionary`2 htmlAttributes)
   at Core.Helpers.ActionLinkHelper.ActionQueryLink(HtmlHelper htmlHelper, String linkText, String action, Object routeValues, IDictionary`2 htmlAttributes) in ActionLinkHelper.cs: line 32
   at Core.Helpers.ActionLinkHelper.ActionQueryLink(HtmlHelper htmlHelper, String linkText, String action, Object routeValues) in ActionLinkHelper.cs: line 16
   at TestSite.UnitTests.Helpers.ActionLinkHeplerTests.ActionLinkHeplerShouldGenerateCorrectActionLink() in ActionLinkHeplerTests.cs: line 51

好吧,对于这样一批代码我真的很抱歉。 但我正在处理这个问题大约 3 天。如您所见,错误甚至不发生在某些 MVC 库中,而是发生在 System.Web.UI.Util。 .即使我能找到 System.Web.UI.Util源并将其作为另一个项目添加到我的解决方案中,我无法强制 MVC 框架使用此项目而不是 System.Web.UI.Util来自全局组装现金。老实说,在我的解决方案中将 MVC 从 GAC 替换为 MVC 的源项目甚至非常困难,因为它非常复杂,有很多依赖关系,当我尝试这样做时,我遇到了很多错误,其中大部分是已经使用来自全局装配现金的 MVC 装配的外部库。同样最重要的是,我的辅助方法在我的项目中运行良好,它仅在测试时调用异常。所以我的建议是helper的测试条件不全或者可能是错误的。总而言之,我的问题是如何使用 Moq 为我的 html 帮助程序扩展方法模拟正确的条件,或者,是否还有其他问题?

最佳答案

事实证明,要测试依赖路由信息的助手,需要模拟以下 RequestContext.HttpContext 方法:

  • RequestContext.HttpContext.Request.ApplicationPath - 应返回类似于根路径的内容(即 @"/")
  • RequestContext.HttpContext.Response.ApplyAppPathModifier - 可以简单地返回其输入参数:

示例:

request.Setup(r => r.ApplicationPath).Returns(@"/");
response.Setup(r => r.ApplyAppPathModifier(It.IsAny<string>()))
                .Returns((string s) => s);

关于c# - 如果我在测试时在 System.Web.UI.Util 处遇到异常,如何对我的 HtmlHelper 扩展方法进行单元测试?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12401749/

相关文章:

asp.net-mvc-3 - ASP.NET MVC 3 无法识别 .cshtml View 文件

wpf - 单元测试 MVVM 命令

asp.net-mvc-3 - 在注册时添加用户角色(表单例份验证)MVC3

unit-testing - 安装go lang后无法运行go test程序

iphone - iOS UnitTest 目标停止运行我的 SenTestCases,为什么?

c# - Unity3d Swipe 跳转不是很灵敏

c# - 在 Blob 存储中组织 Azure 媒体 Assets

c# - 从两个角度算出顺时针转还是逆时针转

c# - Crystal 报表和登录问题

asp.net-mvc - 在IIS 7.5上托管的ASP.Net MVC 3/4默认处理程序映射