Take the 2-minute tour ×
Stack Overflow is a question and answer site for professional and enthusiast programmers. It's 100% free, no registration required.

I have written method that converts MVC view to string and test method to check if it returns string.

Of course it works with Web but when I run test in NUnit it throws a NullReferenceException in System.Web when method tries to call View.Render.

Here is StackTrace:

   w System.Web.VirtualPath.GetCacheKey()
   w System.Web.Compilation.BuildManager.GetCacheKeyFromVirtualPath(VirtualPath virtualPath, Boolean& keyFromVPP)
   w System.Web.Compilation.BuildManager.GetVPathBuildResultFromCacheInternal(VirtualPath virtualPath, Boolean ensureIsUpToDate)
   w System.Web.Compilation.BuildManager.GetVPathBuildResultInternal(VirtualPath virtualPath, Boolean noBuild, Boolean allowCrossApp, Boolean allowBuildInPrecompile, Boolean throwIfNotFound, Boolean ensureIsUpToDate)
   w System.Web.Compilation.BuildManager.GetVPathBuildResultWithNoAssert(HttpContext context, VirtualPath virtualPath, Boolean noBuild, Boolean allowCrossApp, Boolean allowBuildInPrecompile, Boolean throwIfNotFound, Boolean ensureIsUpToDate)
   w System.Web.Compilation.BuildManager.GetVirtualPathObjectFactory(VirtualPath virtualPath, HttpContext context, Boolean allowCrossApp, Boolean throwIfNotFound)
   w System.Web.Compilation.BuildManager.GetObjectFactory(String virtualPath, Boolean throwIfNotFound)
   w System.Web.Mvc.BuildManagerWrapper.System.Web.Mvc.IBuildManager.FileExists(String virtualPath)
   w System.Web.Mvc.BuildManagerViewEngine.FileExists(ControllerContext controllerContext, String virtualPath)
   w System.Web.Mvc.VirtualPathProviderViewEngine.GetPathFromSpecificName(ControllerContext controllerContext, String name, String cacheKey, String[]& searchedLocations)
   w System.Web.Mvc.VirtualPathProviderViewEngine.GetPath(ControllerContext controllerContext, String[] locations, String[] areaLocations, String locationsPropertyName, String name, String controllerName, String cacheKeyPrefix, Boolean useCache, String[]& searchedLocations)
   w System.Web.Mvc.VirtualPathProviderViewEngine.FindView(ControllerContext controllerContext, String viewName, String masterName, Boolean useCache)
   w System.Web.Mvc.ViewEngineCollection.<>c__DisplayClassc.<FindView>b__b(IViewEngine e)
   w System.Web.Mvc.ViewEngineCollection.Find(Func`2 lookup, Boolean trackSearchedPaths)
   w System.Web.Mvc.ViewEngineCollection.Find(Func`2 cacheLocator, Func`2 locator)
   w System.Web.Mvc.ViewEngineCollection.FindView(ControllerContext controllerContext, String viewName, String masterName)
   w MvcApplication.Infrastructure.Services.MailingService.RenderEmailBody[T](String viewPath, T model, ControllerContext controllerContext, Boolean isParialView) w d:\MyProjects\CertyfikatyNoble\branches\Certyfikaty3\MvcApplication\Infrastructure\Services\MailingService.cs:wiersz 175
   w MvcApplication.Tests.MailingServiceTests.ViewToStrngTest() w d:\MyProjects\CertyfikatyNoble\branches\Certyfikaty3\MvcApplication.Tests\MailingServiceTests.cs:wiersz 144

Rendering method code:

public string RenderEmailBody<T>(string viewPath, T model, ControllerContext controllerContext, bool isParialView)
{
    ViewEngineResult viewEngineResult = null;
    if (isParialView == true)
    {
        viewEngineResult = ViewEngines.Engines.FindPartialView(controllerContext, viewPath);
    }
    else
    {
        viewEngineResult = ViewEngines.Engines.FindView(controllerContext, viewPath, null);
    }

    if (viewEngineResult == null)
    {
        throw new FileNotFoundException("Coukld not find view.");
    }

    var view = viewEngineResult.View;
    controllerContext.Controller.ViewData.Model = model;

    string result = null;

    using (var sw = new StringWriter())
    {
        var ctx = new ViewContext(controllerContext, view,
                                    controllerContext.Controller.ViewData,
                                    controllerContext.Controller.TempData,
                                    sw);
        view.Render(ctx, sw);
        result = sw.ToString();
    }

    return result;
}

And test method with controller context mock:

        Mock<HttpBrowserCapabilitiesBase> browserMock = new Mock<HttpBrowserCapabilitiesBase>();
        browserMock.Setup(m => m.IsMobileDevice).Returns(false);

        Mock<HttpServerUtilityBase> httpServerUtilityBaseMock = new Mock<HttpServerUtilityBase>(MockBehavior.Strict);

        Mock<HttpResponseBase> httpResponseMock = new Mock<HttpResponseBase>(MockBehavior.Strict);
        httpResponseMock.Setup(m => m.Cookies).Returns(new HttpCookieCollection() { new HttpCookie("ResponseCookieTest") });

        Mock<HttpRequestBase> httpRequestMock = new Mock<HttpRequestBase>(MockBehavior.Strict);
        httpRequestMock.Setup(m => m.UserHostAddress).Returns("127.0.0.1");
        httpRequestMock.Setup(m => m.Cookies).Returns(new HttpCookieCollection() { new HttpCookie("RequestCookieTest") });
        httpRequestMock.Setup(m => m.UserAgent).Returns("None");
        httpRequestMock.Setup(m => m.Browser).Returns(browserMock.Object);
        httpRequestMock.Setup(m => m.ApplicationPath).Returns("/");
        httpRequestMock.Setup(m => m.AppRelativeCurrentExecutionFilePath).Returns("/");
        httpRequestMock.Setup(m => m.PathInfo).Returns(string.Empty);
        httpRequestMock.Setup(m => m.Form).Returns(new NameValueCollection());
        httpRequestMock.Setup(m => m.QueryString).Returns(new NameValueCollection());

        Mock<HttpSessionStateBase> httpSessionStateMock = new Mock<HttpSessionStateBase>(MockBehavior.Strict);
        httpSessionStateMock.Setup(m => m.SessionID).Returns(Guid.NewGuid().ToString());


        Mock<HttpContextBase> HttpContextMock = new Mock<HttpContextBase>(MockBehavior.Strict);
        HttpContextMock.Setup(m => m.Request).Returns(httpRequestMock.Object);
        HttpContextMock.Setup(m => m.Response).Returns(httpResponseMock.Object);
        HttpContextMock.Setup(m => m.Server).Returns(httpServerUtilityBaseMock.Object);
        HttpContextMock.Setup(m => m.Session).Returns(httpSessionStateMock.Object);

        HttpContextMock.Setup(m => m.Items).Returns(new ListDictionary());

        RouteData routeData = new RouteData();
        routeData.Values.Add("controller", "someController");
        routeData.Values.Add("action", "index");

        Mock<ControllerContext> controllerContextMock = new Mock<ControllerContext>(MockBehavior.Strict);
        controllerContextMock.Setup(m => m.HttpContext).Returns(HttpContextMock.Object);
        controllerContextMock.Setup(m => m.RouteData).Returns(routeData);
        controllerContextMock.Setup(m => m.Controller).Returns(new AccountController());

        SiteConfigurationService siteConfigurationService = SiteConfigurationService.Instance();
        siteConfigurationService.LoadConfig<SiteConfigurations>(this._siteConfigurationsRepository.GetDefaultConfig());

        MailingService service = new MailingService(controllerContextMock.Object, siteConfigurationService);

        string result = service.RenderEmailBody<object>("/ViewToRenderToString.cshtml", new object(), controllerContextMock.Object, false);

I read HERE that the cause is that _virtualPath and HttpRuntime.AppDomainAppVirtualPathString int System.Web.VirtualPathString are NULL.

So is it possible to set them for unit testing?

Thanks in advance,

share|improve this question
    
I looked into this quite extensively on a previous occasion and it is difficult. What you could do instead is render your email body as usual and read it using a WebRequest or such. Then do a integration test in case you want to see how it renders. –  JuhaKangas Jan 3 '14 at 14:26
    
things I did when I was trying to accomplish this without resorting to a webrequest: turn on allow .net framework stepping and/or download the mvc source code, decompile the assembly that is erroring directly. I don't think I accomplished it, but I'm VERY interested in an answer –  Maslow Mar 11 '14 at 14:07

1 Answer 1

Have you tried implementing your own VirtualPathProvider?

Something like this:

public class CustomVirtualPathProvider : VirtualPathProvider
    {
        internal class CustomVirtualFile : ViewVirtualFile
        {
            public override bool IsDirectory
            {
                get
                {
                    return base.IsDirectory;
                }
            }
            public override string Name
            {
                get
                {
                    return base.Name;
                }
            }
            public override string ResourceKey
            {
                get
                {
                    return base.ResourceKey;
                }
            }
            public override System.IO.Stream Open()
            {
                return base.Open();
            }
            public CustomVirtualFile(string path)
                : base(path)
            {

            }

        }
        public override bool FileExists(string virtualPath)
        {
            return base.FileExists(virtualPath);
        }
        public override VirtualFile GetFile(string virtualPath)
        {
            return base.GetFile(virtualPath);
        }
        public override VirtualDirectory GetDirectory(string virtualDir)
        {
            return base.GetDirectory(virtualDir);
        }
        public override bool DirectoryExists(string virtualDir)
        {
            return base.DirectoryExists(virtualDir);
        }


    }

Then in Global.asax

///register our custom virtual path provider factory.
            HostingEnvironment.RegisterVirtualPathProvider(new CustomVirtualPathProvider());

With this approach, you can render views from anywhere.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.