如何使用Moq模拟控制器上下文

6

我正在尝试使用MOQ框架,但目前遇到了一个障碍。下面的单元测试失败,因为ViewName属性的实际值为空字符串。

请问有谁能指点我正确的方向,为什么这个测试没有通过呢?

[TestMethod]
public void Can_Navigate_To_About_Page()
{
    var request = new Mock<HttpRequestBase>();
    request.Setup(r => r.HttpMethod).Returns("GET");
    var mockHttpContext = new Mock<HttpContextBase>();
    mockHttpContext.Setup(c => c.Request).Returns(request.Object);

    var controllerContext = new ControllerContext(mockHttpContext.Object, 
                                new RouteData(), 
                                new Mock<ControllerBase>().Object);
    var controller = new HomeController();

    controller.ControllerContext = controllerContext;
    var result = controller.About() as ViewResult;

    Assert.AreEqual("About", result.ViewName);
}

以下内容也会返回一个空的视图名称。
        HomeController controller = new HomeController();
        ViewResult result = controller.About() as ViewResult;
        Assert.IsNotNull(result);
        Assert.AreEqual("About", result.ViewName);

从网上展示模拟和良好的测试驱动开发的例子中,我对于让上述第一个单元测试示例工作所需的其他管道感到困惑。

谢谢,

安德鲁

2个回答

7
测试失败的原因是因为在没有显式指定视图名称时决定ViewName的内容位于框架深处,更确切地说是在视图引擎中。因此,在当前情况下进行测试,您需要模拟请求管道的更多部分。我所做的,并且建议的方法是不依赖默认值,而是明确指定视图。
return View("About");

那么,该值将在不模拟任何东西的情况下进行测试:
var controller = new HomeController();
var result = controller.About() as ViewResult;
Assert.AreEqual("About", result.ViewName);

谢谢,看到其中一个方法签名是View(string viewName, object model),你的观点非常有道理!附言:测试通过! :-) - REA_ANDREW

3
这是因为您对MVC框架的工作方式做出了一些假设。如果您依赖于约定来定位视图,则在调用ExecuteResult()之前,框架实际上会将ViewName属性保留为空字符串。
您可以在MVC源代码中的ViewResultBase.ExecuteResult的第68行看到此代码:
if (String.IsNullOrEmpty(ViewName)) {
   ViewName = context.RouteData.GetRequiredString("action");
}

此外,您需要注意测试的内容。通常来说,您应该专注于测试您编写的代码,而不是框架的代码。测试确保按约定解析视图名称是否正确实际上是框架本身的单元测试,而不是基于它构建的应用程序。
如果您正在寻找在MVC中使用模拟的好方法,您可能希望尝试测试您的路由(这有点像您正在尝试做的事情)。您可以查看Phil Haack发布的关于这个主题的文章以帮助您入门。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接