'.NET Core stop HostedService in the Integration test

I have .NET Core web API project, for some reasons, we created a background service in this project and start running the background service while the application is started. So, we created a BackgroundWorkderService, which inherited from BackgroundService (Microsoft.Extensions.Hosting) like below:

public class BackgroundWorkerService : BackgroundService
{
    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        await DoWork(stoppingToken);
    }

    public override async Task StartAsync(CancellationToken cancellationToken)
    {
        await ExecuteAsync(cancellationToken);
    }

    public override Task StopAsync(CancellationToken cancellationToken)
    {
        return Task.CompletedTask;
    }
}

To run it while the application starts, I added the background service to the hosted service in Program.cs as below:

.ConfigureServices(services =>
                services.AddHostedService<BackgroundWorkerService>());

Now, we need to create an integration test, and we want to stop the background service while we are running the integration test.

Does anyone know how to stop it in the integration test? I have tried to remove the service from ConfigureTestServices, but no luck with it, the background service still runs when the integration test starts.



Solution 1:[1]

I found a solution to put the background service register in a condition as below.

Edit the Program.cs file as below in the section of registering your background service:

.ConfigureServices(services =>
        {
            if (Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") != "INTEGRATION")
            {
                services.AddHostedService<BackgroundWorkerService>();
            }
        });

Then try to change the variable to be INTEGRATION from where you need.

Solution 2:[2]

You should be able to, as you mentioned in the question, remove the service in ConfigureTestServices. However, exactly how we specify which service to remove is the key here.

In my experience, you need to find the ServiceDescriptor in question rather than create a new one with the expected values (see note below for why). I use LINQ and the implementation type of the service (in your case, BackgroundWorkerService) to easily find the existing ServiceDescriptor. Then I can remove it from the list, which means it won't be started when CreateClient() is called on the WebApplicationFactory.

Looks something like this:

builder.ConfigureTestServices(services =>
{
    var descriptor = services.Single(s => s.ImplementationType == typeof(BackgroundWorkerService));
    services.Remove(descriptor);
}

A really nice benefit of this approach is that it keeps test logic out of the production code and entirely in the setup/fixture for your tests.

Note on IServiceCollection removals: With a bit of poking, I believe this is because ServiceDescriptor doesn't provide an equality or comparison method other than Object.Equals, which falls back to reference equality. So even if all the values are the same in a new ServiceDescriptor, they would different objects and thus won't be found and removed from the service list.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 halfer
Solution 2 Cameron Bielstein