如果我在系统中遇到异常,如何对我的HtmlHelper扩展方法进行单元测试.网状物UI.测试时使用



嗯。。。问题更为复杂,然后题目说。首先,我有一个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方法在我的项目中运行良好,它只在测试时调用异常。所以我的建议是,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);

最新更新