【问题标题】:Unit testing HtmlHelper extension method fails单元测试 HtmlHelper 扩展方法失败
【发布时间】:2014-08-08 15:03:50
【问题描述】:

我正在尝试测试我编写的一些HtmlHelper 扩展方法。我的第一个问题是如何创建HtmlHelper 实例,但我使用以下代码解决了这个问题:

private static HtmlHelper<T> CreateHtmlHelper<T>(T model)
{
    var viewDataDictionary = new ViewDataDictionary(model);
    var controllerContext = new ControllerContext(new Mock<HttpContextBase>().Object,
    new RouteData(),
    new Mock<ControllerBase>().Object);

    var viewContext = new ViewContext(controllerContext, new Mock<IView>().Object, viewDataDictionary, new TempDataDictionary(), new Mock<TextWriter>().Object);

    var mockViewDataContainer = new Mock<IViewDataContainer>();
mockViewDataContainer.Setup(v => v.ViewData).Returns(viewDataDictionary);

    return new HtmlHelper<T>(viewContext, mockViewDataContainer.Object);
}

我的几个测试现在可以正常工作,但有一个测试会引发异常。测试定义如下:

// Arrange
var inputDictionary = CreateDictionary();
var htmlHelper = CreateHtmlHelper(inputDictionary);

// Act
var actualHtmlString = htmlHelper.EditorFor(m => m.Dict, model).ToHtmlString();
...

EditorFor 方法是我的扩展方法。在该方法的某处,进行了以下调用:

tagBuilder.MergeAttributes(htmlHelper.GetUnobtrusiveValidationAttributes(expression, metadata));    

当从我的单元测试中执行这段代码时,会抛出以下异常:

System.NullReferenceExceptionObject reference not set to an instance of an object.
   at System.Web.Mvc.ViewContext.ScopeCache.Get(IDictionary`2 scope, HttpContextBase httpContext)
   at System.Web.Mvc.ViewContext.get_UnobtrusiveJavaScriptEnabled()
   at System.Web.Mvc.HtmlHelper.GetUnobtrusiveValidationAttributes(String name, ModelMetadata metadata)
   at AspNetMvcDictionarySerialization.HtmlHelperExtensions.InputTagHelper(HtmlHelper htmlHelper, ModelMetadata metadata, InputType inputType, String expression, IDictionary`2 htmlAttributes, String fullName, Int32 index, String fieldType, String val) in HtmlHelperExtensions.cs: line 154

所以代码在ScopeCache.Get 中失败了,但为什么呢?有谁知道如何解决这个问题?

【问题讨论】:

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


    【解决方案1】:

    我最终做的是查看source code of ASP.NET MVC。在他们的代码中,他们还测试了HtmlHelper 实例。他们使用名为MvcHelper 的实用程序类来实现这一点,它提供了方便的方法来创建具有正确准备的HTTP 上下文的新HtmlHelper 实例。

    删除我不需要的代码后,我得到了以下类:

    public static class MvcHelper
    {
        public static HtmlHelper<TModel> GetHtmlHelper<TModel>(TModel inputDictionary)
        {
            var viewData = new ViewDataDictionary<TModel>(inputDictionary);
            var mockViewContext = new Mock<ViewContext> { CallBase = true };
            mockViewContext.Setup(c => c.ViewData).Returns(viewData);
            mockViewContext.Setup(c => c.HttpContext.Items).Returns(new Hashtable());
    
            return new HtmlHelper<TModel>(mockViewContext.Object, GetViewDataContainer(viewData));
        }
    
        public static IViewDataContainer GetViewDataContainer(ViewDataDictionary viewData)
        {
            var mockContainer = new Mock<IViewDataContainer>();
            mockContainer.Setup(c => c.ViewData).Returns(viewData);
            return mockContainer.Object;
        }
    }
    

    有了这个帮助类,我的代码就可以正确执行了。

    我为完整的帮助类创建了一个要点,以便轻松包含在您的项目中:https://gist.github.com/ErikSchierboom/6da474dcd5751fbbc94c

    【讨论】:

    • 老兄你摇滚。这是第一个允许我测试一些辅助方法的方法。我需要找到一些关于模拟 HtmlHelper 的其他 StackOverflow 问题,并说这是更好的答案。
    • 有没有人遇到过该代码的问题?我最终 CachedAssociatedMetadataProvider.PrototypeCache 抛出异常:/
    • 非常感谢。我花了一整天的时间收到NullReferenceExceptions,直到找到你的帖子。
    • Mocking HttpContext.Items 如果你得到“方法或操作未实现”异常也有帮助
    【解决方案2】:

    看起来你可能也需要模拟 HttpContext。

    【讨论】:

    • 你的意思是HttpContext.Current?你有任何示例代码吗?
    猜你喜欢
    • 1970-01-01
    • 2021-12-15
    • 1970-01-01
    • 1970-01-01
    • 2012-09-06
    • 2013-03-05
    • 2019-01-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多