如果在测试时在System.Web.UI.Util中出现异常,如何对我的HtmlHelper扩展方法进行单元测试?

时间:2012-09-13 07:51:09

标签: 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汇编。最重要的是,我的帮助方法在我的项目中工作正常,它只在测试时调用异常。所以我的建议是帮助者的测试条件不完整或可能是错误的。总结,我的问题是如何使用Moq模拟我的html帮助扩展方法的正确条件,或者,是否还有其他一些问题?

1 个答案:

答案 0 :(得分:10)

原来,为了测试依赖路由信息的助手,需要模拟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);