2
votes

I'm trying to use RazorViewToStringRenderer in unit test project. However, when I invoke it, I'm getting errors related to razor compilation:

One or more compilation references are missing. Ensure that your project is referencing 'Microsoft.NET.Sdk.Web' and the 'PreserveCompilationContext' property is not set to false.

The .cshtml files are included in the tested project as embedded resources and unit test project references Microsoft.AspNetCore.All It works when I run the web project, but it does not when I invoke it from unit test project:

var sp = ConfigureServices();
sp.GetService<RazorViewToStringRenderer>();


void ConfigureServices()
{
    var services = new ServiceCollection();
    var applicationEnvironment = PlatformServices.Default.Application;
    services.AddSingleton(applicationEnvironment);
    services.AddSingleton<IHostingEnvironment>(new HostingEnvironment
    {
        WebRootFileProvider = new PhysicalFileProvider(applicationEnvironment.ApplicationBasePath),
        ApplicationName = Path.GetFileName(applicationEnvironment.ApplicationBasePath)
    });
    var diagnosticSource = new DiagnosticListener("Microsoft.AspNetCore");
    services.AddSingleton<DiagnosticSource>(diagnosticSource);
    services.AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>();
    services.AddLogging();
    var embeddedProvider = new EmbeddedFileProvider(typeof(Startup).Assembly); //needed for mailtemplates
    var compositeProvider = new CompositeFileProvider(embeddedProvider);
    services.AddSingleton(compositeProvider);
    services.AddMvc().AddRazorOptions(options => options.FileProviders.Add(compositeProvider));

    services.AddTransient<RazorViewToStringRenderer>();

    return services.BuildServiceProvider();
}

{Microsoft.AspNetCore.Mvc.Razor.Compilation.CompilationFailedException: One or more compilation failures occurred: lfrn3mzv.yfv(4,62): error CS0012: The type 'Attribute' is defined in an assembly that is not referenced. You must add a reference to assembly 'netstandard, Version=2.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51'. lfrn3mzv.yfv(4,81): error CS0518: Predefined type 'System.String' is not defined or imported lfrn3mzv.yfv(4,145): error CS0518: Predefined type 'System.Type' is not defined or imported lfrn3mzv.yfv(4,11): lfrn3mzv.yfv(11,11): error CS0246: The type or namespace name 'System' could not be found (are you missing a using directive or an assembly reference?) lfrn3mzv.yfv(14,36): error CS0234: The type or namespace name 'ViewFeatures' does not exist in the namespace 'Microsoft.AspNetCore.Mvc' (are you missing an assembly reference?) lfrn3mzv.yfv(20,129): error CS0246: The type or namespace name 'TACS' could not be found (are you missing a using directive or an assembly reference?) lfrn3mzv.yfv(20,80): error CS0012: The type 'Object' is defined in an assembly that is not referenced. You must add a reference to assembly 'netstandard, Version=2.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51'. lfrn3mzv.yfv(23,67): error CS1983: The return type of an async method must be void, Task or Task lfrn3mzv.yfv(23,39): error CS0400: The type or namespace name 'System' could not be found in the global namespace (are you missing...

'_AppCode_Mail_Notifications_SponsorReview_SFNegative_cshtml.ExecuteAsync()': not all code paths return a value lfrn3mzv.yfv(20,18): error CS0518: Predefined type 'System.Void' is not defined or imported /AppCode/Mail/Notifications/_ViewImports.cshtml(1,7): error CS0246: The type or namespace name 'TACS' could not be found (are you missing a using directive or an assembly reference?) /AppCode/Mail/Notifications/SponsorReview/SFNegative.cshtml(4,92): error CS0012: The type 'Object' is defined in an assembly that is not referenced. You must add a reference to assembly 'netstandard, Version=2.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51'. /AppCode/Mail/Notifications/SponsorReview/SFNegative.cshtml(4,92): error CS0518: Predefined type 'System.Object' is not defined or imported /AppCode/Mail/Notifications/SponsorReview/SFNegative.cshtml(4,92): error CS0518: Predefined type 'System.Void' is not defined or imported /AppCode/Mail/Notifications/SponsorReview/SFNegative.cshtml(6,38): error CS0012: The type 'Object' is defined in an assembly that is not referenced. You must add a reference to assembly 'netstandard, Version=2.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51'. /AppCode/Mail/Notifications/SponsorReview/SFNegative.cshtml(6,38): error CS0518: Predefined type 'System.Object' is not defined or imported /AppCode/Mail/Notifications/SponsorReview/SFNegative.cshtml(6,38): error CS0518: Predefined type 'System.Void' is not defined or imported at Microsoft.AspNetCore.Mvc.Razor.Internal.RazorViewCompiler.CompileAndEmit(RazorCodeDocument codeDocument, String generatedCode) at Microsoft.AspNetCore.Mvc.Razor.Internal.RazorViewCompiler.CompileAndEmit(String relativePath) at Microsoft.AspNetCore.Mvc.Razor.Internal.RazorViewCompiler.CreateCacheEntry(String normalizedPath) --- End of stack trace from previous location where exception was thrown --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.TaskAwaiter1.GetResult() at Microsoft.AspNetCore.Mvc.Razor.Internal.DefaultRazorPageFactoryProvider.CreateFactory(String relativePath) at Microsoft.AspNetCore.Mvc.Razor.RazorViewEngine.CreateCacheResult(HashSet1 expirationTokens, String relativePath, Boolean isMainPage) at Microsoft.AspNetCore.Mvc.Razor.RazorViewEngine.LocatePageFromPath(String executingFilePath, String pagePath, Boolean isMainPage) at Microsoft.AspNetCore.Mvc.Razor.RazorViewEngine.GetView(String executingFilePath, String viewPath, Boolean isMainPage) at TACS.Web.AppCode.Mail.ViewRenderService.d__4.MoveNext() in C:\Projects\Tacs\TACS\TACS.Web\AppCode\Mail\ViewRenderService.cs:line 42}

1
You could use RazorLight for this kind of task github.com/toddams/RazorLight - wheeler
I've already got working razor rendering. There's no need to use something else. This is just about unit testing - Liero
I believe this won't work because the default engine is too tightly coupled with the mvc runtime. So either you decouple and then mock the engine part or use something that can be run in a unit-test environment. - wheeler

1 Answers

0
votes

The first thing I would check is that your UnitTest project is targetting the identical net.core version as your web project. I say this because even the core types like String and Type are not being found. This says to me that when compiling-at-runtime (which is what razor does) it can't find the core dlls for what it's trying to compile.

The second thing you might need, is to explicitly add references from your UnitTest project to the exact framework dependencies relied on by your razor pages.

Finally, it may be that at runtime your Razor pages are relying on entries in web.config for their namespace imports:

 <system.web.webPages.razor>
    <host factoryType="System.Web.Mvc.MvcWebRazorHostFactory, System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
    <pages pageBaseType="System.Web.Mvc.WebViewPage">
      <namespaces>
        <add namespace="System.Web.Mvc" />
        <add namespace="System.Web.Mvc.Html" />
        <add namespace="System.Web.Routing" />
        <add namespace="A line each for all my other namespaces ..." />
        <add namespace="A line each for all my other namespaces ..." />
        <add namespace="A line each for all my other namespaces ..." />
      </namespaces>
    </pages>
  </system.web.webPages.razor>

In which case, to make it work in unit tests, try removing these lines from the web.config and add the import to each and every razor page:

@import Microsoft.NET.Sdk.Web
@import ....