3

I need to render a view to a string (to send as email). I'm using this implementation.

I want to unit test it, without needing a full ASP.NET Core environment. So I must create an instance of IRazorViewEngine.

The default implementation is RazorViewEngine. I has a mega constructor because each argument needs to be created and each one has a mega constructor, etc., etc. (I can't mock it, I need a live instance.)

Surely there is a simpler way to get an instance?

(Before Core, I could use ViewEngines.Engines. Maybe Core has something similar?)

grokky
  • 8,537
  • 20
  • 62
  • 96

1 Answers1

4

I tried to do this as well with a unit test similar to this and ran into various issues:

var services = new ServiceCollection();
services.AddMvc();
... ended up needing to add random things into the service collection ...
var serviceProvider = services.BuildServiceProvider();
var razorViewEngine = serviceProvider.GetRequiredService<IRazorViewEngine>();

I ended up going with more of a component test approach using Microsoft.AspNetCore.Mvc.Testing:

using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.Razor;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.Extensions.DependencyInjection;
using Xunit;

public class ComponentTestStartup : IStartup
{
    public IServiceProvider ConfigureServices(IServiceCollection services)
    {
        services.AddMvc();
        return services.BuildServiceProvider();
    }

    public void Configure(IApplicationBuilder app)
    {
    }
} 

public class ComponentTestServerFixture : WebApplicationFactory<ComponentTestStartup>
{
    public TService GetRequiredService<TService>()
    {
        if (Server == null)
        {
            CreateDefaultClient();
        }

        return Server.Host.Services.GetRequiredService<TService>();
    }

    protected override IWebHostBuilder CreateWebHostBuilder()
    {
        var hostBuilder = new WebHostBuilder();
        return hostBuilder.UseStartup<ComponentTestStartup>();
    }

    // uncomment if your test project isn't in a child folder of the .sln file
    // protected override void ConfigureWebHost(IWebHostBuilder builder)
    // {
    //    builder.UseSolutionRelativeContentRoot("relative/path/to/test/project");
    // }
}

public class RazorViewToStringRendererTests
{
    private readonly RazorViewToStringRenderer viewRenderer;

    public RazorViewToStringRendererTests()
    {
        var server = new ComponentTestServerFixture();
        var serviceProvider = server.GetRequiredService<IServiceProvider>();
        var viewEngine = server.GetRequiredService<IRazorViewEngine>();
        var tempDataProvider = server.GetRequiredService<ITempDataProvider>();
        viewRenderer = new RazorViewToStringRenderer(viewEngine, tempDataProvider, serviceProvider);
    }

    [Fact]
    public async Task CanRenderViewToString()
    {
        // arrange
        var model = "test model";

        // act
        var renderedView = await viewRenderer.RenderViewToStringAsync("/Path/To/TestView.cshtml", model);

        // assert
        Assert.NotNull(renderedView);
        Assert.Contains(model, renderedView, StringComparison.OrdinalIgnoreCase);
    }
}

TestView.cshtml:

@model string
<h1>@Model</h1>
bkaid
  • 51,465
  • 22
  • 112
  • 128
  • The code above is not compiling. e.g. `Server` is undefined. Do you have a github repo for this? – Icerman Jan 07 '20 at 18:56
  • 1
    @Icerman the code above was for .net core 2.2. I just created a github repo with an example on .net core 3.1 here: https://github.com/bkaid/razor-email-example – bkaid Jan 11 '20 at 23:47