在使用MVCContrib对MVC 3控制器和视图进行单元测试时,向RouteData添加键和值

本文关键字:单元测试 键和值 添加 RouteData 视图 MVCContrib MVC 控制器 | 更新日期: 2023-09-27 18:28:38

好的,所以我使用MVCContrib TestHelper来对我的控制器进行单元测试,效果很好。

不过,和很多人一样,我在这里所说的单元测试实际上是指集成测试,我想至少确保我的视图在提供的模型下呈现时没有错误。。。否则,即使我在测试控制器,我也会错过一整类基本上与模型相关的错误(比如如果模型属性为null,则视图不会呈现)。

不管怎样,我开始想办法做到这一点(也就是在谷歌上搜索怎么做)。似乎最简单的方法是构建一个HTMLHelper,并让它只呈现视图(在这种情况下是部分视图)。

不幸的是,当我尝试使用我的模拟HTMLHelper时,它抱怨它在路由数据中没有可用的控制器名称。

果不其然,我看了看,控制器RouteData没有填充。不幸的是,RouteData.Values RouteValueDictionary是只读的,所以我不能只提供必要的值。

我并没有接受HTMLHelper的想法来解决将视图作为测试的一部分来实际呈现的问题,所以可以随意提出替代方案,但请不要建议我使用Selenium、Watin或其他UI测试工具来测试我的视图。。。我希望控件能够处理和恢复一些测试的状态和数据信息,而这在基于UI的测试中是做不到的。

以下是我目前正在使用的代码,试图渲染部分:

    public class FakeView : IView
{
    #region IView Members
    public void Render(ViewContext viewContext, System.IO.TextWriter writer)
    {
        throw new NotImplementedException();
    }
    #endregion
}
public class WebTestUtilities
{
    public static void prepareCache()
    {
        SeedDataManager seed = new SeedDataManager();
        seed.CheckSeedDataStatus();
    }
    public static string RenderRazorViewToString(string viewName, object model, Controller controller)
    {
        var sb = new StringBuilder();
        var memWriter = new StringWriter(sb);
        var html = new HtmlHelper(new ViewContext(controller.ControllerContext,
            new FakeView(), new ViewDataDictionary(), new TempDataDictionary(), memWriter),
            new ViewPage());
        //This fails because it can't extract route information like the controller name)
        html.RenderPartial(viewName, model);
        return sb.ToString();
    }

    public void setupTestEnvironment(Controller controller)
    {
        RouteTable.Routes.Clear();
        RouteTable.Routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
        RouteTable.Routes.MapRoute(
            "Default", 
            "{controller}/{action}/{id}", 
            new { controller = "Main", action = "DefaultView", id = UrlParameter.Optional } 
        );


        ModelBinders.Binders[typeof(DateTime)] = new DateTimeModelBinder();
        ModelBinders.Binders[typeof(DateTime?)] = new DateTimeModelBinder();
        ModelMetadataProviders.Current = new DateTimeMetadataProvider();

    }
}

这是我的测试方法:

        [TestMethod]
    public void GetUserTableView()
    {
        ViewResult result = controller.UserTable() as ViewResult;
        //I can populate the route and handler on the controller...
        RouteData routes = RouteTable.Routes.GetRouteData(controller.HttpContext);
        controller.RouteData.Route = routes.Route;
        controller.RouteData.RouteHandler = routes.RouteHandler;
        RouteValueDictionary routeKeys = new RouteValueDictionary();
        routeKeys.Add("controller", "UserManagement");
        routeKeys.Add("action", "UserTable");
        //But the RouteData.Values collection is read only :(
        controller.RouteData = new RouteData(){Values = routeKeys};
        string renderedView = WebTestUtilities.RenderRazorViewToString(result.ViewName, result.Model, controller);
    }

顺便说一句,我得到的具体错误是:RouteData必须包含名为"controller"且字符串值为非空的项。

在使用MVCContrib对MVC 3控制器和视图进行单元测试时,向RouteData添加键和值

您可能已经解决了这个问题,但我刚刚遇到了类似的问题,并通过重置控制器上的ControllerContext来解决设置RouteData的问题,如下所示:

RouteData routeData = new RouteData();
routeData.Values.Add("someRouteDataProperty", "someValue");
ControllerContext controllerContext = new ControllerContext { RouteData = routeData };
controller.ControllerContext = controllerContext;

然后在控制器RouteData.Values.ContainsKey("someRouteDataProperty")中按照您在测试中设置的方式工作。

这之所以有效,是因为ControllerContext有一个无参数构造函数,它故意允许模仿等。

您可以重写ControllerControllerContext.RouteData-它的虚拟和RouteData属性只是使用它来读取。

来自asp.net mvc RouteData属性的反射代码:

public RouteData RouteData 
{ 
    get 
    {
        return ControllerContext == null ? null : ControllerContext.RouteData; 
    }
}