Search code examples
c#asp.net-core.net-coreintegration-testing

Controllers Not Mapping on WebApplicationFactory for TestClient .NET 6


So I'm upgrading a bunch of our APIs and migrating them to the new shiny top-level statement style of startup.

These projects all also have integration tests that rely upon a WebApplicationFactory to create a TestClient for them. For the most-part, retargetting that at Program rather than Startup has worked just fine.

However, on one of my APIs, I just get 404s whenever I try to call a controller and I can't for the life of me work out why.

If I add in a minimal app.MapGet("test", () => "test") I can hit that from the test, so I guess for some reason, the controllers are getting unmapped. That being said, I'm struggling to see much difference between this API and the other ones.

So, all of my APIs follow the same basic pattern in Program:

var builder = WebApplication.CreateBuilder();

BuildConfiguration();
ConfigureLogging();
ConfigureXRay();
ConfigureServices();
ConfigureHost();

var app = builder.Build();
ConfigureApp();

app.Run();

I haven't included everything for brevity, but in ConfigureServices() I believe this line should add and configure all of the controllers:

void ConfigureServices()
{
    builder.Services.ConfigureHealthChecks();
    builder.Services.ConfigureAppMetrics();

    builder.Services.ConfigureFromAppSettingsConfig(builder.Configuration);
    builder.Services.ConfigureIOCServices(builder.Configuration);

    builder.Services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies());
    builder.Services.AddControllers(opt => opt.AddGlobalErrorHandling()); //<----- This one
    builder.Services.AddSoapCore();

    builder.Services.ConfigureSwagger();
}

Then in ConfigureApp() I've got this call which I believe should map the controllers:

    app.UseEndpoints(endpoints =>
                     {
                         endpoints.MapControllers();
                         endpoints.UseSoapEndpoint<ICorporateActionsSoapService>("/investments/api/CorporateActions.asmx", new BasicHttpBinding());
                     });

So if I run the app directly, all of that seems to work. For my tests, I've got the following.

public class CustomWebApplicationFactory : WebApplicationFactory<Program>
{
    public CustomWebApplicationFactory()
    {
        SetAppSettingsValues();
        DisableMetricsBuiltFlag();
    }

    private void DisableMetricsBuiltFlag()
    {
        var metricsBuiltField = typeof(MetricsAspNetHostBuilderExtensions).GetField("_metricsBuilt",
                                                                                    BindingFlags.Static |
                                                                                    BindingFlags.NonPublic);
        metricsBuiltField?.SetValue(null, false);
    }

    private void SetAppSettingsValues()
    {
        Environment.SetEnvironmentVariable("IntegrationTests", "true");
    }
}

And then this is injected into my test classes as an IClassFixture<CustomWebApplicationFactory> by XUnit. I then replace a few of the services with mocks and create a test client.

public GlobalErrorHandlingIntegrationTest(CustomWebApplicationFactory factory)
{
    _mockLogger       = _fixture.Create<ILogger<CorporateActionsController>>();
    _mockAuditService = _fixture.Create<ICorporateActionsEmailAuditService>();

    _mockEmailService = _fixture.Create<ICorporateActionsEmailService>();
    _mockEmailService.GetEmails().Returns(Task.Run(() => _fixture.Create<CorporateActionResponse>()));
    _appFactory = factory
       .WithWebHostBuilder
           (
            builder =>
             {
                builder.ConfigureTestServices
                    (
                     x =>
                     {
                          x.Add(new ServiceDescriptor(typeof(ILogger<CorporateActionsController>), _mockLogger));
                          x.Add(new ServiceDescriptor(typeof(ICorporateActionsEmailService),       _mockEmailService));
                          x.Add(new ServiceDescriptor(typeof(ICorporateActionsEmailAuditService),  _mockAuditService));
                      }
                     );
             }
            );
   _testClient = _appFactory.CreateClient();
}

And then all of my tests using that _testClient just 404. Any idea what's going wrong here and why these controllers might be getting unmapped?


I've seen this question but the answer doesn't seem to apply to my case.


Solution

  • You have to pass the args to the WebApplication.CreateBuilder(args);