NUnit测试MVC Controller返回null视图

时间:2011-10-10 18:36:42

标签: asp.net-mvc-3 view controller nunit

我的HomeController.Index()操作有效(在正常操作中),但在NUnit测试中,返回的ActionResult(ViewResult)始终具有空View和ViewName。

以下是我正在运行的测试(为了便于阅读,将其压缩为单个方法)。

  • 我正在使用Moq,NUnit,Castle.Windsor
  • 结果的模型是正确的,但没有与结果相关联的视图。
  • 除了最后一个断言之外,所有断言都通过,它引用了result.View。

重复清晰 - 在正常操作中返回正确的视图。

[Test]
public void WhenHomeControllerIsInstantiated()
{
    Moch mochRepository = new Mock<IRepository>();
    mochRepository.Setup(s => s.Staff.GetStaffByLogonName("twehr"))
                  .Returns(new Staff { StaffID = 5, LogonName = @"healthcare\twehr" });

    IController controller = new HomeController(mochRepository.Object);

    IPrincipal FakeUser = new GenericPrincipal(new GenericIdentity("twehr", "Basic"), null);
    var result = ((HomeController)controller).Index(FakeUser) as ViewResult;

    Assert.IsNotNull(controller);
    Assert.IsInstanceOf(typeof(HomeController), controller);

    Assert.IsInstanceOf(typeof(HomeViewModel), ((ViewResult)result).Model);

    // result.View and result.ViewName are always null
    Assert.AreEqual("Index", result.ViewName);
}

显然,我在测试设置中忽略了一些东西,却找不到它。任何帮助表示赞赏。

2 个答案:

答案 0 :(得分:3)

result.View为null的原因是因为你还没有在控制器的上下文中执行视图结果,你只需在测试中直接调用action方法,该方法返回一个由MVC准备执行的ViewResult框架。

result.ViewName为null的原因是您没有在action方法中明确指定它。

MVC框架在返回的ViewResult上调用ExecuteResult(ControllerContext context),然后填充ViewName(如果为null)并通过调用FindView(context)来搜索要渲染的视图,该视图填充视图。

在这里查看MVC代码,您可能会稍微了解一下:

// System.Web.Mvc.ViewResultBase
public override void ExecuteResult(ControllerContext context)
{
    if (context == null)
    {
        throw new ArgumentNullException("context");
    }
    if (string.IsNullOrEmpty(this.ViewName))
    {
        this.ViewName = context.RouteData.GetRequiredString("action");
    }
    ViewEngineResult viewEngineResult = null;
    if (this.View == null)
    {
        viewEngineResult = this.FindView(context);
        this.View = viewEngineResult.View;
    }
    TextWriter output = context.HttpContext.Response.Output;
    ViewContext viewContext = new ViewContext(context, this.View, this.ViewData, this.TempData, output);
    this.View.Render(viewContext, output);
    if (viewEngineResult != null)
    {
        viewEngineResult.ViewEngine.ReleaseView(context, this.View);
    }
}

正如Zasz所述,如果您在控制器中返回一个明确指定ViewName的ViewResult,那么您可以在测试中测试它。

E.g。而不是

return View(model);

DO

return View("Index", model);

另外,我同意Zasz的第一点,你的测试有一些奇怪的断言和许多不必要的演员。我发现编写这些类型的测试最简洁的方法是:

HomeController controller = new HomeController();

ViewResult result = controller.Index() as ViewResult;
Assert.IsNotNull(result); // Asserts that result is of type ViewResult since it will be null otherwise

// TODO: Add assertions on the model
// ...

答案 1 :(得分:2)

你的考试很奇怪。对我来说,这些陈述看起来毫无意义:

Assert.IsNotNull(controller);
Assert.IsInstanceOf(typeof(HomeController), controller);

你在这里测试什么?这是什么在这里,使用上面的界面:

((HomeController)controller)

为什么要使用上面的界面?为什么当你可以根据你的情况更改它以返回ViewResult时,Index方法返回ActionResult(我猜)?具体,只有当Action可以返回多种结果时才使用超类ActionResult。您可以避免此演员((ViewResult)result)

作为对你问题的回答:

只有在控制器中显式调用View()方法时,才会填充

result.ViewName等:

View("Index", model)

如果简单地调用View(),则依赖于框架来根据约定呈现正确的视图 - 而不要需要测试框架功能 - 所以只检查内容模型。信任MVC以呈现正确的视图 - MVC不会填充ViewName属性,而是检查属性是否为null,如果是,则继续并使用约定来呈现视图,否则呈现您指定的内容。