Home > OS >  WebApplication.Services.GetRequiredService<MyController> Fails in Unit Test
WebApplication.Services.GetRequiredService<MyController> Fails in Unit Test

Time:12-07

I want to verify dependencies are properly configured for all Controllers in an ASP.NET 6 Web App.

Assuming I have invoked .AddControllersAsServices(), I can call app.Services.GetRequiredService<HomeController>() from Main and it succeeds.

public class Program
{
    public static WebApplication BuildApp(string[] args)
    {
        var builder = WebApplication.CreateBuilder(args);
        builder.Services.AddControllersWithViews()
                        .AddControllersAsServices();
        var app = builder.Build();
        return app;
    }

    public static void Main(string[] args)
    {
        WebApplication app = BuildApp(args);
        app.Services.GetRequiredService<HomeController>();      // This succeeds!
        // <snip>
        app.Run();
    }
}

But it fails when called from a unit test.

    [Fact]
    public void Test1()
    {
        var app = Program.BuildApp(Array.Empty<string>());
        app.Services.GetRequiredService<HomeController>();      // This fails!
    }

System.InvalidOperationException: 'No service for type 'TryControllersAsServices.Controllers.HomeController' has been registered.'

What is the difference between calling from Main versus a unit test?

CodePudding user response:

It was fun and interesting. Long story short, add this line to Program.cs to make it work

var builder = WebApplication.CreateBuilder(args);
builder.Environment.ApplicationName = typeof(Program).Assembly.FullName; // <- this

Longer read

  1. With any test runner (either Visual Studio or Rider) it's going under dotnet test. The dotnet test command builds the solution and runs a test host application for each test project in the solution. The test host executes tests in the given project using a test framework, for example xUnit.
  2. As it's running in a separate tool, it uses this tool as an entry point and set this runner library as an application name, not your library name.
  3. .NET Core on startup do population and initialization based on ApplicationName
  4. As a result, your controllers are not found and not registered.

As a possible workaround you could use -e argument for dotnet test from docs (dotnet test -e ASPNETCORE_APPLICATIONNAME=SO_74704705), but unfortunately, it will not work. So the easiest way is to add line to override ApplicationName during application startup. You can try to examine github repo and this particular issue with another possible workarounds if it does not work (eg add additional Part Manager, dynamically load, etc)

  • Related