【问题标题】:Unit testing my controller method results in an empty ViewName?对我的控制器方法进行单元测试会导致 ViewName 为空?
【发布时间】:2009-04-05 06:28:06
【问题描述】:

我正在对我的标准进行一些简单的 MS 单元测试,没有什么特别的控制器。

当我从返回的ViewResult 对象中检查ViewName 属性时,它是""(空)。

我的印象是 ViewName 隐含在 View 的名称中(如 ASP.NET MVC 控制器测试中的 suggested by this MS article)。

顺便说一句,当我测试 ViewData 时,一切都在那里并且正确。

这是我的代码...

public ActionResult Index(int? page, string tag)
{
    if (page == null || page <= 0)
    {
        page = 1;
    }

    var viewData = new IndexViewData
                       {
                       ... my property setters, etc ...
                       };
    return View(viewData);
}

[TestMethod]
public void Index_Action_Should_Return_Index_View_For_Default_HomePage()
{
    // Arrange.
    var controller = PostController; // Wrapper, cause I use D.I.

    // Act.
    ViewResult viewResult = controller.Index(null, null) as ViewResult;

    // Assert.
    Assert.IsNotNull(viewResult);
    Assert.AreEqual("Index", viewResult.ViewName); // This is false/fails.

    var indexViewData = viewResult.ViewData.Model as IndexViewData;
    Assert.IsNotNull(indexViewData); // This is true.
}

【问题讨论】:

    标签: asp.net-mvc unit-testing controller


    【解决方案1】:

    ViewName 仅在您在 ViewResult 中设置时出现。如果您的视图名称与您的控制器名称匹配,那么我会检查以确保 ViewName 为空或空,因为这将是(IMO)正确的行为,因为您不想在视图上设置名称。我只在我打算返回的视图与操作不匹配时检查是否设置了 ViewName——例如,在返回“错误”视图时。

    编辑:以下是 ViewResultBase.cs 中 ExecuteResult 的源代码(来自 RC1,我的 Macintosh 上没有 RTW 的源代码)。如您所见,它会检查 ViewName 是否已直接设置,如果没有,它会从控制器上下文的路由数据中的操作中提取它。这只发生在 ExecuteResult 中,它在控制器的操作完成后调用。

        public override void ExecuteResult(ControllerContext context) {
            if (context == null) {
                throw new ArgumentNullException("context");
            }
            if (String.IsNullOrEmpty(ViewName)) {
                ViewName = context.RouteData.GetRequiredString("action");
            }
    
            ViewEngineResult result = null;
    
            if (View == null) {
                result = FindView(context);
                View = result.View;
            }
    
            ViewContext viewContext = new ViewContext(context, View, ViewData, TempData);
            View.Render(viewContext, context.HttpContext.Response.Output);
    
            if (result != null) {
                result.ViewEngine.ReleaseView(context, View);
            }
        }
    

    【讨论】:

    • 嘿嘿。唔。我不明白你在说什么。 “仅当您在 ViewResult 中设置 ViewName 时才会出现”。我以为框架会自动设置这个?你是说它不是自动设置的?
    • 不。据我所知,除非您特别设置,否则 ViewName 为空。我相信,如果它为 null 或为空,它会从操作名称中提取视图名称。
    • >>我相信如果它为 null 或为空,它会从操作名称中提取视图名称。
    • 没有。我的意思是,为了选择视图,它会检查 ViewResult 中的 ViewName 是否为空或空,如果是,它会选择与操作同名的视图。 ViewName 将为空,除非您在 ViewResult 中专门设置它。我会从源头贴出相关代码
    • 啊啊啊啊啊啊啊啊啊!!!!我现在明白了:) 所以如果我确实返回了 View("Index", viewdata),那么 viewname 就会被设置!因为(在我原来的例子中)我刚刚返回了视图数据,它通过方法的名称找出了视图!啊!明白了! :)
    【解决方案2】:

    我个人觉得 MVC2 提供的测试工具有些笨拙。我猜想已经存在更好的东西了,但我最终创建了一个简单的类来测试动作。我在一个由优秀的开源Java MVC framework Stripes 提供的名为MockRoundTrip 的类上建模了接口(实现是另一回事)。

    这里是测试动作时用来获取动作目标页面的方法,叫做getTripDestination()。无论视图名称是否明确设置,它都会返回正确的结果

        //Get the destination page of the request, using Runtime execution pattern of MVC, namely
        //if no ViewName is explicitly set in controller, ViewResult will have an empty ViewName
        //Instead, current action name will be used in its place
        public string getTripDestination()
        {
           RouteData routeData = getRouteData();
           ViewResult viewResult = (result is ViewResult) ? (ViewResult)result : null;
           string tripDestination = (viewResult != null) ? viewResult.ViewName : "";
    
           return (tripDestination != "") ? tripDestination : (String)routeData.Values["action"];
        }
    
        private RouteData getRouteData()
        {
           HttpContextBase context = controller.ControllerContext.RequestContext.HttpContext;
           return RouteTable.Routes.GetRouteData(context);
        }
    

    【讨论】:

      【解决方案3】:

      视图名称由框架自动设置。但是当我们进行单元测试时,我们将框架短路,并且没有任何东西可以设置名称。

      所以我们的操作需要在单元测试时明确设置视图名称。如果我们真的非常想依赖约定,我们也可以检查 null 或空。

      【讨论】:

        【解决方案4】:

        documentation for Controller.View() 声明:

        View类的这个方法重载返回一个ViewResult对象 具有空的 ViewName 属性。如果您正在编写单元测试 控制器动作,考虑空的 ViewName 属性 不采用字符串视图名称的单元测试。

        在运行时,如果 ViewName 属性为空,则当前操作 name 用于代替 ViewName 属性。

        所以当期望一个与当前操作同名的视图时,我们可以测试它是一个空字符串。

        或者,Controller.View(String) 方法将设置 ViewName。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2015-06-08
          • 1970-01-01
          • 1970-01-01
          • 2013-05-31
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-06-02
          相关资源
          最近更新 更多