Search code examples
testing.net-coreasp.net-core-webapixunitasp.net-core-6.0

Why WebApplicationFactory is saving state from previous builds?


I will try to show my problem with a sample code easier to understand.

I have used WebApplicationFactory to develop my acceptance tests. Let's say that I have the typical minimal Program.cs with the following line to register one of my modules:

builder.Services.RegisterModule<StartupRegistrationModule>(builder.Configuration, builder.Environment);

And this module is declared like this:

internal sealed class StartupRegistrationModule : IServiceRegistrationModule
{
    public static Dictionary<string, string> _dictionary = new();

    public void Register(IServiceCollection services, IConfiguration configuration, IHostEnvironment hostEnvironment)
    {
        // Lot of modules being registered
        _dictionary.Add("key", "value");
    
    }
}

One of my tests file is like this:

public sealed class MyTests : AcceptanceTestBase
{

    [Fact]
    public void Test1()
    {
        // arrange
        // act
        // assert
    }

    [Fact]
    public void Test2()
    {
        // arrange
        // act
        // assert
    }

    [Fact]
    public void Test3()
    {
        // arrange
        // act
        // assert
    }
}

And AcceptanceTestBase is:

public abstract class AcceptanceTestBase : IDisposable
{
    protected HttpClient _httpClient;
    protected WebApplicationFactory<Program> _webApplicationFactory;

    public AcceptanceTestBase()
    {
        _webApplicationFactory = new WebApplicationFactory<Program>()
        .WithWebHostBuilder(builder =>
        {
            // ... Configure test services
        });

        _httpClient = _webApplicationFactory.CreateClient();
    }

    public void Dispose()
    {
        _httpClient.Dispose();
        _webApplicationFactory.Dispose();
    }
}

If I try to execute all these tests my tests will fail in the second test run because the WebApplicationFactory is trying to build again the Application but it already has the key in the dictionary and it will fail. See the image for more understanding on the problem.

enter image description here

So my question is, how can I build the application in different scopes to do not share this dictionary state?

Thanks :)

Update: The real static dictionary is saved behind this nuget package that keeps the track of all my circuit breaker policies state. I do not actually need even the HttpClients for my tests but did not find a way to remove them and not load this. I tried removing all the HttpClients to see if it also removes their dependencies, but it does not seem to make the trick. enter image description here


Solution

  • It is because you are using:

    internal sealed class StartupRegistrationModule : IServiceRegistrationModule
    {
        /// .. static here
        public static Dictionary<string, string> _dictionary = new();
    
        public void Register(IServiceCollection services, IConfiguration configuration, IHostEnvironment hostEnvironment)
        {
            // Lot of modules being registered
            _dictionary.Add("key", "value");
        
        }
    }
    

    The static Dictionary is shared over all your tests because they run in the same process. Each test starts a new (Test-)WebHost but the dictionary remains untouched.

    My proposal is to not use statics anywhere in DI context to prevent such hidden traps.

    I don't know the purpose of your Dictionary here but maybe you can extract this to a singleton registration which you can replace in your (Test.)WebHost on each new test / startup?