WebApplicationFactory and TestServer in integration tests for ASP.NET Core

Viewed 606

I have two integ test classes defined as below. When I run tests in ApiControllerIT, all runs successfully. The same for FoundationControllerIT. However, when I run both together (by running the enclosing folder), tests fail.

The error message is:

Scheduler with name 'DefaultQuartzScheduler' already exists.

I have this definition in my Startup.cs file:

services.AddSingleton (IHostedService, QuartzHostedService);

So obviously this line causes the issue (if I remove this line, all testing together runs OK). So my question is - I'm a newbie from Java.. so I don't have a very good insight into .NET Core Integ test framework, but my understanding is - TestServer is created for each of test classes, e.g. One TestServer for ApiControllerIT, and the other for FoundationControllerIT. Is this incorrect? I'm just frustrated how come I'm getting a message:

Scheduler with name 'DefaultQuartzScheduler' already exists.

when I run two separate test classes?? How come the TestServers interfere each other?

public class ApiControllerIT : IClassFixture<WebApplicationFactory<Startup>>
{
    private readonly WebApplicationFactory<Startup> _factory;

    public ApiControllerIT(WebApplicationFactory<Startup> factory)
    {
        _factory = factory;
    }
    
    // tests ...

}

public class FoundationControllerIT : IClassFixture<WebApplicationFactory<Startup>>
{
    private readonly WebApplicationFactory<Startup> _factory;

    public FoundationControllerIT(WebApplicationFactory<Startup> factory)
    {
        _factory = factory;
    }
    
    // tests ...

}
1 Answers

I might a bit late on this but I also had this problem and it might still be useful for others in the future.

The problem comes because the WebApplicationFactory will create two instances of your Startup class. This is drastically different from your normal service start, where you only have one instance of Startup.

(It might be a bit different in your case, but I am using a Singleton instance to create and manage my Schedulers throughout my application.)

The WebApplicationFactory also calls ConfigureServices and Configure on both of them. So even your singletons will be there twice, one for each Startup instance. This is not a problem because the Startup instances will have their own ServiceProvider. It only comes to problems if (multiple) singleton instances access the same static properties of something. In our case, this is the SchedulerBuilder using SchedulerFactory using SchedulerRepository within Quartz, which is a >real< singleton and uses this code:

/// <summary>
/// Gets the singleton instance.
/// </summary>
/// <value>The instance.</value>
public static SchedulerRepository Instance { get; } = new SchedulerRepository();

This means that your independent Singleton classes still use the same SchedulerRepository within Quartz, which explains why you get the exception.


Depending on what you are testing within your tests you have some options to tackle this issue.

  1. The SchedulerRepository has a lookup method, which you could use to check if the Scheduler was already created by another instance: public virtual Task<IScheduler?> Lookup(string schedName, CancellationToken cancellationToken = default) - So you either just take the existing Scheduler or you generate another one with a different name
  2. Catch the exception and do nothing. (this only really makes sense if your tests do not need Quartz, which is probably unlikely but I still wanted to list this option)

I cannot tell you what makes most sense in your case as this is completely dependent on what your application does and what you want to test, but I will probably stick with one variant of option 1.

Related