In a dotnet 5 web api project, how do I get the instance of a singleton service in a test method?

Viewed 204

I'm integration testing my asp.net 5 web apis. In my test methods I want to get a reference to the instance a of faked service so I can customise the service's behaviour for my tests.

In the app, the service is scoped as singleton.

//in ConfigureServices in Startup.cs    
services.AddSingleton<IYbNewProjectsService, YbNewProjectsService>();

In my test project, I set up my client and server in my TestFixture class, and save a reference to the ServiceProvider, returned to me by the services.BuildServiceProvider() call...

private void SetupClient()
{
    Client = Factory.WithWebHostBuilder(builder =>
    {
        builder.ConfigureTestServices(services =>
        {
            services.Remove<IYbNewProjectsService>();
            services.AddSingleton<IYbNewProjectsService, FakeYbNewProjetsService>();
        });

        builder.ConfigureServices(services =>
        {
            ServiceProvider = services.BuildServiceProvider();
        });
    })
    .CreateClient(new WebApplicationFactoryClientOptions
    {
        AllowAutoRedirect = false
    });
}

I know it's working, because in my tests, when I do...

// ServiceProvider is inherited from TestFixture, so it's the same as above.
var fakeYbService = (FakeYbNewProjectsService)ServiceProvider.GetService(typeof(IYbNewProjectsService));
fakeYbService.InjectedResponse = @"[{{""idProjet"":1883557,""dateDApplication"":...
var response = await Client.GetAsync("/api/yb-new-projects");

...I get my faked service. Trouble is, when I call Client.GetAsync(), the TestServer is using a clean instance of the same faked service, without my InjectedResponse. This is meant to be a singleton, so why are there two of them?

1 Answers

You don't need to setup a ServiceProvider yourself, it's available on the Factory:

// Use this:
Factory.Services.GetService(typeof(IYbNewProjectsService));

// Instead of:
ServiceProvider.GetService(typeof(IYbNewProjectsService));

It works correctly for me with this setup (.NET 5 Razor pages app + .NET 5 XUnit test project):

// In app Startup.ConfigureServices:
services.AddSingleton<ITestService, TestService>();

// Test
[Fact]
public async Task Test1()
{
    var factory = new WebApplicationFactory<Startup>()
        .WithWebHostBuilder(builder =>
        {
            builder.ConfigureTestServices(services =>
            {
                services.AddSingleton<ITestService, FakeTestService>();
            });
        });

    var svc = factory.Services.GetService<ITestService>();
    svc.Do();

    var client = factory.CreateDefaultClient();

    await client.GetAsync("http://localhost");
}

The endpoint it calls also gets the ITestService and calls the method. In both cases I hit the breakpoint in the FakeTestService.

One thing you could check is that does the Startup.ConfigureServices run before the ConfigureTestServices? In my case it does which allows us to overwrite the service. If they were to run in the other order, your test service would get overwritten. In this case you would need to use TryAddSingleton instead in your Startup class.

Related