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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/9831463/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-09 10:49:17  来源:igfitidea点击:

Add Keys and Values to RouteData when using MVCContrib to unit test MVC 3 controllers and Views

c#asp.net-mvcasp.net-mvc-3razor

提问by Amasuriel

Okay so I am using MVCContrib TestHelper to unit test my controllers, which works great.

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

Like so many people though, by unit test I really mean integration test here and I want to at least make sure my views render without error given the model provided...otherwise I can miss a whole class of bugs basically related to the model even though I am testing the controller (like the view not rendering if a model property is null).

像很多人一样,通过单元测试,我在这里的真正意思是集成测试,我想至少确保我的视图在提供模型的情况下没有错误地呈现......否则我可能会错过一整类基本上与模型相关的错误尽管我正在测试控制器(例如,如果模型属性为空,则视图不会呈现)。

Anyway I started trying to figure out how to do this (aka googling how to do it). It seemed like the easiest way was to construct an HTMLHelper and have it just render the views (partial in this case).

无论如何,我开始试图弄清楚如何做到这一点(又名谷歌搜索如何做到这一点)。似乎最简单的方法是构造一个 HTMLHelper 并让它只呈现视图(在这种情况下是部分视图)。

Unfortunately when I try to use my mocked HTMLHelper it complains that is doesn't have the controller name available in the route data.

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

Sure enough, I look and the controllers RouteData is not populated. Unfortunately the RouteData.Values RouteValueDictionary is read only, so I can't just supply the necessary values.

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

I'm not married to the HTMLHelper idea to solve the problem of actually rendering the view as part of the test, so feel free to suggest alternatives there, but please don't bother suggesting I test my Views using Selenium, Watin or other UI testing tools...I want the control to be able to do things like manipulate and restore state and data info for some of the tests, which I cannot do with UI based testing.

我不认同 HTMLHelper 的想法来解决实际渲染视图作为测试的一部分的问题,所以请随意在那里建议替代方案,但请不要打扰建议我使用 Selenium、Watin 或其他 UI 测试我的视图测试工具...我希望控件能够为某些测试执行诸如操作和恢复状态和数据信息之类的操作,而基于 UI 的测试则无法做到这一点。

Here is the code I am currently using to try to render the partial:

这是我目前用来尝试呈现部分的代码:

    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();



    }
}

And here is my test method:

这是我的测试方法:

        [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);
    }

BTW, the specific error I get is : The RouteData must contain an item named 'controller' with a non-empty string value.

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

采纳答案by stevie_c

You may have already worked this out, but I just had a similar problem and solved setting the RouteData by resetting the ControllerContext on the controller like so:

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

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

and then in the controller RouteData.Values.ContainsKey("someRouteDataProperty") works as you've set up in the test.

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

This works because there's a parameterless constructor of the ControllerContext that's deliberately there to allow mocking etc.

这是有效的,因为有一个 ControllerContext 的无参数构造函数,它故意在那里允许模拟等。

回答by Sly

You can override Controllers ControllerContext.RouteData- its virtual and RouteData property is just using it to read from.

您可以覆盖控制器ControllerContext.RouteData- 它的虚拟和 RouteData 属性只是使用它来读取。

From the reflected code of asp.net mvc RouteData property:

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

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