public static string Render(this WebViewPage view, HttpContextBase httpContext, object model = null) { StringWriter writer = new StringWriter(); view.Initialize(httpContext, writer); view.ViewData.Model = model; WebPageContext webPageContext = new WebPageContext(view.ViewContext.HttpContext, null, model); // Using private reflection to access some internals // Also make sure the use the same writer used for initializing the ViewContext in the OutputStack // Note: ideally we would not have to do this, but WebPages is just not mockable enough :( // Add the writer to the output stack PropertyInfo outputStackProp = typeof(WebPageContext).GetProperty("OutputStack", BindingFlags.Instance | BindingFlags.NonPublic); Stack <TextWriter> outputStack = (Stack <TextWriter>)outputStackProp.GetValue(webPageContext, null); outputStack.Push(writer); // Push some section writer dictionary onto the stack. We need two, because the logic in WebPageBase.RenderBody // checks that as a way to make sure the layout page is not called directly PropertyInfo sectionWritersStackProp = typeof(WebPageContext).GetProperty("SectionWritersStack", BindingFlags.Instance | BindingFlags.NonPublic); Stack <Dictionary <string, SectionWriter> > sectionWritersStack = (Stack <Dictionary <string, SectionWriter> >)sectionWritersStackProp.GetValue(webPageContext, null); Dictionary <string, SectionWriter> sectionWriters = new Dictionary <string, SectionWriter>(StringComparer.OrdinalIgnoreCase); sectionWritersStack.Push(sectionWriters); sectionWritersStack.Push(sectionWriters); // Set the body delegate to do nothing PropertyInfo bodyActionProp = typeof(WebPageContext).GetProperty("BodyAction", BindingFlags.Instance | BindingFlags.NonPublic); bodyActionProp.SetValue(webPageContext, (Action <TextWriter>)(w => { }), null); // Set the page context on the view (the property is public, but the setter is internal) PropertyInfo pageContextProp = typeof(WebPageRenderingBase).GetProperty("PageContext", BindingFlags.Instance | BindingFlags.Public); pageContextProp.SetValue(view, webPageContext, BindingFlags.NonPublic, null, null, null); // Execute/render the view view.Execute(); return(writer.ToString()); }
public static RazorViewExecutionResult ExecuteTemplate(WebViewPage <T> viewPage, T model, HttpContextBase httpContext) { var result = new RazorViewExecutionResult(); // Warning: lots of mocking below // mock HTTP state objects var context = new Mock <HttpContextBase>(); context.Setup(x => x.Items).Returns(new Dictionary <object, object>()); var response = new Mock <HttpResponseBase>(); response.Setup(x => x.ApplyAppPathModifier(It.IsAny <string>())) .Returns(new Func <string, string>(x => x)); context.Setup(x => x.Response).Returns(response.Object); var request = new Mock <HttpRequestBase>(); context.Setup(x => x.Request).Returns(request.Object); request.Setup(x => x.ApplicationPath).Returns("/"); request.Setup(x => x.IsLocal).Returns(true); var requestRouteContext = new RequestContext(context.Object, new RouteData()); // mock page view context var view = new Mock <ViewContext>(); view.Setup(x => x.HttpContext).Returns(context.Object); var viewMock = new Mock <IView>(); view.Setup(x => x.View).Returns(viewMock.Object); view.Setup(x => x.TempData).Returns(new TempDataDictionary()); view.Setup(x => x.ViewData).Returns(new ViewDataDictionary <T>(model)); var viewDataContainer = new Mock <IViewDataContainer>(); // mock view data used by the page viewDataContainer.Setup(c => c.ViewData).Returns(new ViewDataDictionary <T>(model)); // mock html helper var html = new Mock <HtmlHelper <T> >(view.Object, viewDataContainer.Object); var urlHelper = new Mock <UrlHelper>(requestRouteContext); // mock viewengine (for partial views fake resolution) var viewEngine = new Mock <IViewEngine>(); viewEngine.Setup(x => x.FindPartialView( It.IsAny <ControllerContext>(), It.IsAny <string>(), It.IsAny <bool>())) .Returns(new ViewEngineResult(viewMock.Object, viewEngine.Object)); using (var tw = new StringWriter()) using (var twNull = new StringWriter()) // this writer is used to discard unnecessary results { view.Setup(x => x.Writer).Returns(tw); // inject mocked context viewPage.Context = httpContext; var pageContext = new WebPageContext(context: context.Object, page: null, model: null); viewPage.ViewContext = view.Object; if (model != null) { viewPage.ViewData = new ViewDataDictionary <T>(model); } viewPage.Html = html.Object; viewPage.Url = urlHelper.Object; // insert mocked viewEngine for fake partial views resolution ViewEngines.Engines.Clear(); ViewEngines.Engines.Add(viewEngine.Object); // prepare view for execution, discard all generated results viewPage.PushContext(pageContext, twNull); viewPage.ExecutePageHierarchy(pageContext, twNull); // inject textwriter viewPage.OutputStack.Push(tw); // execute compiled page viewPage.Execute(); // find all sections and render them too var dynMethod = pageContext.GetType().GetProperty("SectionWritersStack", BindingFlags.NonPublic | BindingFlags.Instance); var res = (Stack <Dictionary <string, SectionWriter> >)dynMethod.GetValue(pageContext, null); foreach (var section in res.Peek()) { section.Value(); result.SectionNames.Add(section.Key); } result.Text = tw.ToString(); return(result); } }