Skip to content

[Test Collection Cleanup Failure (Controller Test Collection)]: System.InvalidOperationException : The logger is already frozen #46199

@khteh

Description

@khteh

Is there an existing issue for this?

  • I have searched the existing issues

Describe the bug

My CustomWebApplicationFactory need to cleanup the database in Dispose:

namespace Web.Api.IntegrationTests;
public class CustomWebApplicationFactory<TStartup> : WebApplicationFactory<TStartup> where TStartup : class
{
    private IServiceCollection _services;
    private ServiceProvider _sp;
    protected override void ConfigureWebHost(IWebHostBuilder builder)
    {
        Environment.SetEnvironmentVariable("ASPNETCORE_ENVIRONMENT", "IntegrationTests");
        builder.ConfigureServices((context, services) =>
        {
            // Create a new service provider.
            var descriptor = services.SingleOrDefault(d => d.ServiceType == typeof(DbContextPool<AppDbContext>));
            services.Remove(descriptor);
            descriptor = services.SingleOrDefault(d => d.ServiceType == typeof(DbContextPool<AppIdentityDbContext>));
            services.Remove(descriptor);
            services.AddDbContextPool<AppIdentityDbContext>(options =>
            {
                options.UseNpgsql(context.Configuration.GetConnectionString("IntegrationTests"), b => b.MigrationsAssembly("Web.Api.Infrastructure"));
                options.EnableSensitiveDataLogging();
                options.EnableDetailedErrors();
                options.LogTo(Console.WriteLine);
            })
                .AddDbContextPool<AppDbContext>(options =>
                {
                    options.UseNpgsql(context.Configuration.GetConnectionString("IntegrationTests"), b => b.MigrationsAssembly("Web.Api.Infrastructure"));
                    options.EnableSensitiveDataLogging();
                    options.EnableDetailedErrors();
                    options.LogTo(Console.WriteLine);
                });
            services.AddLogging();
            services.AddOptions();
            services.Configure<GrpcConfig>(context.Configuration.GetSection(nameof(GrpcConfig)));
            services.AddScoped<SignInManager<AppUser>>();
            services.AddScoped<ILogger<UserRepository>>(provider =>
            {
                ILoggerFactory loggerFactory = provider.GetRequiredService<ILoggerFactory>();
                return loggerFactory.CreateLogger<UserRepository>();
            });
            services.AddDistributedMemoryCache();
            _services = services;
            // Build the service provider.
            _sp = services.BuildServiceProvider();

            // Create a scope to obtain a reference to the database contexts
            using (var scope = _sp.CreateScope())
            {
                var scopedServices = scope.ServiceProvider;
                var appDb = scopedServices.GetRequiredService<AppDbContext>();
                var identityDb = scopedServices.GetRequiredService<AppIdentityDbContext>();
                var logger = scopedServices.GetRequiredService<ILogger<CustomWebApplicationFactory<TStartup>>>();

                // Ensure the database is created.
                appDb.Database.EnsureCreated();
                identityDb.Database.EnsureCreated();

                try
                {
                    // Seed the database with test data.
                    SeedData.PopulateTestData(identityDb, appDb);
                }
                catch (Exception ex)
                {
                    logger.LogError(ex, $"An error occurred seeding the database with test messages. Error: {ex.Message}");
                }
            }
        });
    }
    public void Dispose()
    {
        //var sp = _services.BuildServiceProvider();
        // Create a scope to obtain a reference to the database contexts
        using (var scope = _sp.CreateScope())
        {
            var scopedServices = scope.ServiceProvider;
            var appDb = scopedServices.GetRequiredService<AppDbContext>();
            var identityDb = scopedServices.GetRequiredService<AppIdentityDbContext>();
            SeedData.CleanUpTestData(identityDb, appDb);
        }
        base.Dispose();
        GC.SuppressFinalize(this);
    }
}

and it throws the following exception:

Failed Web.Api.IntegrationTests.Controllers.ProtectedControllerIntegrationTests.CanAccessProtectedResourceAfterLogin [1 ms]
  Error Message:
   [Test Collection Cleanup Failure (Controller Test Collection)]: System.InvalidOperationException : The logger is already frozen.
  Stack Trace:
     at Serilog.Extensions.Hosting.ReloadableLogger.Freeze()
   at Serilog.SerilogHostBuilderExtensions.<>c__DisplayClass3_1.<UseSerilog>b__1(IServiceProvider services)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSiteMain(ServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitRootCache(ServiceCallSite callSite, RuntimeResolverContext context)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(ServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.Resolve(ServiceCallSite callSite, ServiceProviderEngineScope scope)
   at Microsoft.Extensions.DependencyInjection.ServiceProvider.CreateServiceAccessor(Type serviceType)
   at System.Collections.Concurrent.ConcurrentDictionary`2.GetOrAdd(TKey key, Func`2 valueFactory)
   at Microsoft.Extensions.DependencyInjection.ServiceProvider.GetService(Type serviceType, ServiceProviderEngineScope serviceProviderEngineScope)
   at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider provider, Type serviceType)
   at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService[T](IServiceProvider provider)
   at Serilog.SerilogHostBuilderExtensions.<>c__DisplayClass3_1.<UseSerilog>b__3(IServiceProvider services)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSiteMain(ServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitRootCache(ServiceCallSite callSite, RuntimeResolverContext context)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(ServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.Resolve(ServiceCallSite callSite, ServiceProviderEngineScope scope)
   at Microsoft.Extensions.DependencyInjection.ServiceProvider.CreateServiceAccessor(Type serviceType)
   at System.Collections.Concurrent.ConcurrentDictionary`2.GetOrAdd(TKey key, Func`2 valueFactory)
   at Microsoft.Extensions.DependencyInjection.ServiceProvider.GetService(Type serviceType, ServiceProviderEngineScope serviceProviderEngineScope)
   at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetService[T](IServiceProvider provider)
   at Microsoft.EntityFrameworkCore.Diagnostics.Internal.ScopedLoggerFactory.Create(IServiceProvider internalServiceProvider, IDbContextOptions contextOptions)
   at Microsoft.EntityFrameworkCore.Infrastructure.EntityFrameworkServicesBuilder.<>c.<TryAddCoreServices>b__8_0(IServiceProvider p)
   at ResolveService(ILEmitResolverBuilderRuntimeContext, ServiceProviderEngineScope)
   at ResolveService(ILEmitResolverBuilderRuntimeContext, ServiceProviderEngineScope)
   at ResolveService(ILEmitResolverBuilderRuntimeContext, ServiceProviderEngineScope)
   at ResolveService(ILEmitResolverBuilderRuntimeContext, ServiceProviderEngineScope)
   at ResolveService(ILEmitResolverBuilderRuntimeContext, ServiceProviderEngineScope)
   at ResolveService(ILEmitResolverBuilderRuntimeContext, ServiceProviderEngineScope)
   at Microsoft.Extensions.DependencyInjection.ServiceProvider.GetService(Type serviceType, ServiceProviderEngineScope serviceProviderEngineScope)
   at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider provider, Type serviceType)
   at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService[T](IServiceProvider provider)
   at Microsoft.EntityFrameworkCore.Infrastructure.EntityFrameworkServicesBuilder.<>c.<TryAddCoreServices>b__8_8(IServiceProvider p)
   at ResolveService(ILEmitResolverBuilderRuntimeContext, ServiceProviderEngineScope)
   at ResolveService(ILEmitResolverBuilderRuntimeContext, ServiceProviderEngineScope)
   at Microsoft.Extensions.DependencyInjection.ServiceProvider.GetService(Type serviceType, ServiceProviderEngineScope serviceProviderEngineScope)
   at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetService[T](IServiceProvider provider)
   at Microsoft.EntityFrameworkCore.DbContext.Microsoft.EntityFrameworkCore.Infrastructure.IResettableService.ResetState()
   at Microsoft.EntityFrameworkCore.Internal.DbContextPool`1.Return(IDbContextPoolable context)
   at Microsoft.EntityFrameworkCore.Internal.DbContextLease.Release()
   at Microsoft.EntityFrameworkCore.Internal.ScopedDbContextLease`1.System.IDisposable.Dispose()
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngineScope.Dispose()
   at Web.Api.IntegrationTests.CustomWebApplicationFactory`1.Dispose() in /root/workspace/test/Web.Api.IntegrationTests/CustomWebApplicationFactory.cs:line 92

Using the code suggested in #46162 results in the followinng error:
Many functions are unavailable: SingleOrDefault, context, Remove, AddDbContextPool, etc:

    public static async Task<CustomWebApplicationFactory<TStartup>> CreateWebApplicationFactory()
    {
        Environment.SetEnvironmentVariable("ASPNETCORE_ENVIRONMENT", "IntegrationTests");
        var factory = new CustomWebApplicationFactory<TStartup>();
            // Create a new service provider.
            var descriptor = factory.Services.SingleOrDefault(d => d.ServiceType == typeof(DbContextPool<AppDbContext>));
            factory.Services.Remove(descriptor);
            descriptor = factory.Services.SingleOrDefault(d => d.ServiceType == typeof(DbContextPool<AppIdentityDbContext>));
            factory.Services.Remove(descriptor);
            factory.Services.AddDbContextPool<AppIdentityDbContext>(options =>
            {
                options.UseNpgsql(context.Configuration.GetConnectionString("IntegrationTests"), b => b.MigrationsAssembly("Web.Api.Infrastructure"));
                options.EnableSensitiveDataLogging();
                options.EnableDetailedErrors();
                options.LogTo(Console.WriteLine);
            })
                .AddDbContextPool<AppDbContext>(options =>
                {
                    options.UseNpgsql(context.Configuration.GetConnectionString("IntegrationTests"), b => b.MigrationsAssembly("Web.Api.Infrastructure"));
                    options.EnableSensitiveDataLogging();
                    options.EnableDetailedErrors();
                    options.LogTo(Console.WriteLine);
                });
            factory.Services.AddLogging();
            factory.Services.AddOptions();
            factory.Services.Configure<GrpcConfig>(context.Configuration.GetSection(nameof(GrpcConfig)));
            factory.Services.AddScoped<SignInManager<AppUser>>();
            factory.Services.AddScoped<ILogger<UserRepository>>(provider =>
            {
                ILoggerFactory loggerFactory = provider.GetRequiredService<ILoggerFactory>();
                return loggerFactory.CreateLogger<UserRepository>();
            });
            factory.Services.AddDistributedMemoryCache();
        
        // Create a scope to obtain a reference to the database contexts
        using (var scope = factory.Services.CreateScope())
        {
            var scopedServices = scope.ServiceProvider;
            var appDb = scopedServices.GetRequiredService<AppDbContext>();
            var identityDb = scopedServices.GetRequiredService<AppIdentityDbContext>();
            var logger = scopedServices.GetRequiredService<ILogger<CustomWebApplicationFactory<TStartup>>>();

            // Ensure the database is created.
            appDb.Database.EnsureCreated();
            identityDb.Database.EnsureCreated();

            try
            {
                // Seed the database with test data.
                await SeedData.PopulateTestDataAsync(identityDb, appDb);
            }
            catch (Exception ex)
            {
                logger.LogError(ex, $"An error occurred seeding the database with test messages. Error: {ex.Message}");
                throw;
            }
        }
        return factory;
    }

And I need to set the ASPNETCORE_ENVIRONMENT and doing so in your suggested private constructor results in the following exception!

 Failed Web.Api.IntegrationTests.SignalR.ChatHubTests.ReceiveMessageTest [1 ms]
  Error Message:
   System.AggregateException : One or more errors occurred. (Collection fixture type 'Web.Api.IntegrationTests.CustomWebApplicationFactory`1[[Program, Web.Api, Version=7.0.0.0, Culture=neutral, PublicKeyToken=null]]' may only define a single public constructor.) (The following constructor parameters did not have matching fixture data: CustomWebApplicationFactory`1 factory)
---- Collection fixture type 'Web.Api.IntegrationTests.CustomWebApplicationFactory`1[[Program, Web.Api, Version=7.0.0.0, Culture=neutral, PublicKeyToken=null]]' may only define a single public constructor.
---- The following constructor parameters did not have matching fixture data: CustomWebApplicationFactory`1 factory
  Stack Trace:
  
----- Inner Stack Trace #1 (Xunit.Sdk.TestClassException) -----

----- Inner Stack Trace #2 (Xunit.Sdk.TestClassException) -----

  Failed Web.Api.IntegrationTests.SignalR.ChatHubTests.ReceiveMessageFromUserTest [1 ms]
  Error Message:
   System.AggregateException : One or more errors occurred. (Collection fixture type 'Web.Api.IntegrationTests.CustomWebApplicationFactory`1[[Program, Web.Api, Version=7.0.0.0, Culture=neutral, PublicKeyToken=null]]' may only define a single public constructor.) (The following constructor parameters did not have matching fixture data: CustomWebApplicationFactory`1 factory)
---- Collection fixture type 'Web.Api.IntegrationTests.CustomWebApplicationFactory`1[[Program, Web.Api, Version=7.0.0.0, Culture=neutral, PublicKeyToken=null]]' may only define a single public constructor.
---- The following constructor parameters did not have matching fixture data: CustomWebApplicationFactory`1 factory
  Stack Trace:
  
----- Inner Stack Trace #1 (Xunit.Sdk.TestClassException) -----

----- Inner Stack Trace #2 (Xunit.Sdk.TestClassException) -----

Expected Behavior

No response

Steps To Reproduce

No response

Exceptions (if any)

No response

.NET Version

7.0.102

Anything else?

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions