Nathan Ikazuyir
Nathan Ikazuyir

Reputation: 15

IServiceProvider is immediately disposed after creation and i don't understand why

This is my test class(well, part that contains the problem)

public class FiltrationServiceTests: 
    IClassFixture<IntegrationTestWebAppFactory>,
    IAsyncLifetime
{
    private readonly AppDbContext _db;
    private readonly Func<Task> _resetDb;

    public FiltrationServiceTests(IntegrationTestWebAppFactory factory)
    {
        factory
            .Services
            .CreateScope()
            .ServiceProvider
            .GetRequiredService<AppDbContext>();
        _db = factory.Db;
        _resetDb = factory.ResetDatabase;
    }

Using debugger i can see IServiceProvider which is returned by .Services line is disposed immediately in the constructor. I don't understand why.

IntegrationTestWebAppFactory:

public class IntegrationTestWebAppFactory:
    WebApplicationFactory<Program>,
    IAsyncLifetime
{
    private readonly PostgreSqlContainer _postgresContainer = new PostgreSqlBuilder()
        .WithImage("postgres:latest")
        .WithDatabase("db")
        .WithUsername("postgres")
        .WithPassword("postgres")
        .WithWaitStrategy(Wait.ForUnixContainer().UntilCommandIsCompleted("pg_isready"))
        .WithCleanUp(true)
        .Build();

    public AppDbContext Db = null!;
    private Respawner _respawner = null!;
    private DbConnection _connection = null!;
    
    public async Task ResetDatabase()
    {
        await _respawner.ResetAsync(_connection);
    }
    
    protected override void ConfigureWebHost(IWebHostBuilder builder)
    {
        builder.ConfigureTestServices(services =>
        {
            var descriptorType =
                typeof(DbContextOptions<AppDbContext>);

            var descriptor = services
                .SingleOrDefault(s => s.ServiceType == descriptorType);

            if (descriptor is not null)
            {
                services.Remove(descriptor);
            }
            
            services.AddDbContext<AppDbContext>(options =>
                options.UseNpgsql(_postgresContainer.GetConnectionString(), x =>
                {
                    x.MigrationsAssembly(Assembly.GetAssembly(typeof(Program))!.FullName);
                }));
        });
    }

    public async Task InitializeAsync()
    {
        await _postgresContainer.StartAsync();
        
        Db = Services.CreateScope().ServiceProvider.GetRequiredService<AppDbContext>();
        
        await Db.Database.EnsureDeletedAsync();
        // applying migrations
        await Db.Database.MigrateAsync();
        
        // initializing respawner
        _connection = Db.Database.GetDbConnection();
        await _connection.OpenAsync();
        _respawner = await Respawner.CreateAsync(_connection, new RespawnerOptions
        {
            DbAdapter = DbAdapter.Postgres,
            SchemasToInclude = ["public"],
            WithReseed = true
        });
    }

    public async Task DisposeAsync()
    {
        await _connection.CloseAsync();
        await _postgresContainer.StopAsync();
        await Db.DisposeAsync();
        await base.DisposeAsync();
    }
}

I tried to add Db property into Fixture class and use it in the test class, but DbContext isn't thread safe so can't rely on this

I forgot to mention my problem, i really need IServiceProvider since it can provide me with a new DbContext instances for each service instance i'm testing

Upvotes: 0

Views: 55

Answers (0)

Related Questions