MVC 4 如何模拟 HttpContext - 如何模拟 DisplayModeProvider

10

目前我正在使用Scott Hanselman的HTTP上下文模拟来进行单元测试。这对于MVC 3效果很好,从未后悔过,我用它来测试以下代码。

public class PartialViewRenderer : IPartialViewRenderer
{
    public string Render(Controller controller, string viewName, object model)
    {
        if (string.IsNullOrEmpty(viewName))
            viewName = controller.ControllerContext.RouteData.GetRequiredString("action");

        controller.ViewData.Model = model;

        using (StringWriter sw = new StringWriter())
        {
            ViewEngineResult viewResult = ViewEngines.Engines.FindPartialView(controller.ControllerContext, viewName);
            ViewContext viewContext = new ViewContext(controller.ControllerContext, viewResult.View,
                                                      controller.ViewData, controller.TempData, sw);
            viewResult.View.Render(viewContext, sw);

            return sw.GetStringBuilder().ToString();
        }
    }
}

当我第一次将我的应用程序转换为MVC 4时,遇到了问题,它会出现运行时异常。因此,我尝试着去修复需要修复的内容,这让我改变了Hanselmans MockHelpers上面的以下方法:(基本上是改变了HttpContext.Items的返回方式,因为它会抛出“null”的异常)

 public static HttpContextBase FakeHttpContext()
    {
        var context = new Mock<HttpContextBase>();
        var request = new Mock<HttpRequestBase>();
        var response = new Mock<HttpResponseBase>();
        var session = new Mock<HttpSessionStateBase>();
        var server = new Mock<HttpServerUtilityBase>();
        var cookies = new HttpCookieCollection();
        var items = new ListDictionary();

        request.Setup(r => r.Cookies).Returns(cookies);
        response.Setup(r => r.Cookies).Returns(cookies);

        context.Setup(ctx => ctx.Items).Returns(items);

        context.SetupGet(ctx => ctx.Request).Returns(request.Object);
        context.SetupGet(ctx => ctx.Response).Returns(response.Object);
        context.SetupGet(ctx => ctx.Session).Returns(session.Object);
        context.SetupGet(ctx => ctx.Server).Returns(server.Object);

        return context.Object;
    }

 public static void SetFakeControllerContext(this Controller controller, RouteData route)
    {
        var httpContext = FakeHttpContext();

        ControllerContext context = new ControllerContext(new RequestContext(httpContext, route), controller);

        controller.ControllerContext = context;
    }

这里是一个非常简单的nUnit测试,我要试图确定我需要对这个模拟的http上下文做出哪些更改(我甚至还没有放入断言)。

    [Test]
    public void test()
    {
        _contactsController = _container.Resolve<ContactsController>();

        var route = new RouteData();

        route.Values.Add("controller", "ContactsController");
        route.Values.Add("action", "GetEditContactDetailsDialog");

        _contactsController.SetFakeControllerContext(route);

        var result = _contactsController.GetEditContactDetailsDialog("1");
    }

当我运行这个测试时,它在ViewEngineResult viewResult = ViewEngines.Engines.FindPartialView(controller.ControllerContext, viewName);的 PartialViewRenderer.Render 调用上失败了。以下是堆栈跟踪信息。

at System.Web.WebPages.DisplayModeProvider.<.ctor>b__2(HttpContextBase context) at System.Web.WebPages.DefaultDisplayMode.CanHandleContext(HttpContextBase httpContext) at System.Web.WebPages.DisplayModeProvider.<>c__DisplayClass6.b__5(IDisplayMode mode) at System.Linq.Enumerable.WhereListIterator1.MoveNext() at System.Collections.Generic.List1..ctor(IEnumerable1 collection) at System.Linq.Enumerable.ToList[TSource](IEnumerable1 source) at System.Web.WebPages.DisplayModeProvider.GetAvailableDisplayModesForContext(HttpContextBase httpContext, IDisplayMode currentDisplayMode) at System.Web.Mvc.VirtualPathProviderViewEngine.GetPath(ControllerContext controllerContext, String[] locations, String[] areaLocations, String locationsPropertyName, String name, String controllerName, String cacheKeyPrefix, Boolean useCache, String[]& searchedLocations)

看起来我无法进入并模拟 DisplayModeProvider。根据 MVC 源代码,有人有解决此问题的方法吗?我一直无法在任何地方找到解决方案。


我遇到了一个类似的问题,涉及模拟DisplayMode。不知道你能否找到任何解决方案? - Shahin
2个回答

6

首先,使用MockBehavior.Strict初始化模拟对象,如下:

var context = new Mock<HttpContextBase>(MockBehavior.Strict);

使用此功能可以查找与DisplayMode相关的依赖项。
其次,DisplayMode是ASP.NET MVC 4中的新功能:

http://www.asp.net/whitepapers/mvc4-release-notes#_Toc303253810

所以,它获取 Request.Browser 参数来确定使用哪个视图。为浏览器添加模拟。
var browser = new Mock<HttpBrowserCapabilitiesBase>(MockBehavior.Strict);
var context = new Mock<HttpContextBase>(MockBehavior.Strict);
var request = new Mock<HttpRequestBase>(MockBehavior.Strict);
var response = new Mock<HttpResponseBase>(MockBehavior.Strict);
var session = new Mock<HttpSessionStateBase>(MockBehavior.Strict);
var server = new Mock<HttpServerUtilityBase>(MockBehavior.Strict);
var cookies = new HttpCookieCollection();
var items = new ListDictionary();

browser.Setup(b => b.IsMobileDevice).Returns(false);

request.Setup(r => r.Cookies).Returns(cookies);
request.Setup(r => r.ValidateInput());
request.Setup(r => r.UserAgent).Returns("Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.64 Safari/537.11");
response.Setup(r => r.Cookies).Returns(cookies);

request.Setup(r => r.Browser).Returns(browser.Object);
context.Setup(ctx => ctx.Items).Returns(items);

我还没有让它正常工作。现在我又遇到了另一个错误,希望今晚能解决它。 - Etch
调用ViewEngineCollection.FindPartialView()时似乎缺少某些内容,因为在System.Web.Web.Pages.FileExcistenceCache构造函数中会出现NullReferenceException错误。这个答案在其他情况下可能非常有效,但在我观察到的情况下却不是(尚未)。 - Manfred

0

DisplayMode 实现了接口 IDisplayMode,因此您应该能够使用 Moq 创建一个模拟对象进行注入。 DisplayModeProvider 有一个受保护的 SetDisplayMode 方法,您可以通过反射调用它。


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