MVC 4 Mocking HttpContext - 如何模拟DisplayModeProvider

时间:2012-11-12 17:10:38

标签: .net asp.net-mvc-4 nunit moq

我目前正在使用Scott Hanselmans HTTP context mock进行单元测试。这适用于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调用进行轰炸。以下是堆栈跟踪。

  

在System.Web.WebPages.DisplayModeProvider。&lt; .ctor&gt; b__2(HttpContextBase context)      在System.Web.WebPages.DefaultDisplayMode.CanHandleContext(HttpContextBase httpContext)      在System.Web.WebPages.DisplayModeProvider。&lt;&gt; c__DisplayClass6.b__5(IDisplayMode模式)      at System.Linq.Enumerable.WhereListIterator 1.MoveNext() at System.Collections.Generic.List 1..ctor(IEnumerable 1 collection) at System.Linq.Enumerable.ToList[TSource](IEnumerable 1 source)      在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 []&searchLocations)

似乎我无法进入并模拟DisplayModeProvider。 Per the MVC source Code有没有人有解决方案?我无法在任何地方找到解决方案。

2 个答案:

答案 0 :(得分: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

因此,对于使用确定的View,它会获得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);

答案 1 :(得分:0)

DisplayMode实现接口IDisplayMode,因此您应该能够(使用Moq)创建一个要注入的模拟。 DisplayModeProvider有一个SetDisplayMode(受保护)方法,您可以通过Reflection调用。