我的HomeController.Index()操作有效(在正常操作中),但在NUnit测试中,返回的ActionResult(ViewResult)始终具有空View和ViewName。
以下是我正在运行的测试(为了便于阅读,将其压缩为单个方法)。
重复清晰 - 在正常操作中返回正确的视图。
[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);
}
显然,我在测试设置中忽略了一些东西,却找不到它。任何帮助表示赞赏。
答案 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,如果是,则继续并使用约定来呈现视图,否则呈现您指定的内容。