You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用WebApplicationFactory的集成测试抛出IServiceProvider已释放异常

问题描述

健康检查测试代码

public class HealthCheckEndpointTests : IClassFixture<ItemsApplicationFactory>
{
    private readonly ItemsApplicationFactory _factory;

    public HealthCheckEndpointTests(ItemsApplicationFactory factory)
    {
        _factory = factory;
    }

    public async Task HealthCheck_Test()
    {
       // Arrange
       HttpClient httpClient = _factory.CreateClient();

       // Act 
       string response = await httpClient.GetStringAsync("/health/live");

       // Assert
       Assert.Equal("Healthy", response);
    }
}

ItemsApplicationFactory实现代码

public class ItemsApplicationFactory : WebApplicationFactory<Program>
{
    protected override void ConfigureWebHost(IWebHostBuilder builder)
    {
        builder.ConfigureKestrel(options => options.AllowSynchronousIO = true);


        builder.ConfigureServices(services =>
        {
            // 移除已存在的DbContext配置
            var dbContextDescriptor = services.SingleOrDefault(d => d.ServiceType == typeof(DbContextOptions<ItemsDbContext>));
            if (dbContextDescriptor != null)
                services.Remove(dbContextDescriptor);

            var serviceCollection = new ServiceCollection()
                .AddEntityFrameworkInMemoryDatabase()
                .BuildServiceProvider();

            services.AddDbContext<ItemsDbContext>(mariaDb =>
            {
                mariaDb.UseInMemoryDatabase("template");
                mariaDb.UseInternalServiceProvider(serviceCollection);
            });
        });
    }
}

测试抛出的异常

System.ObjectDisposedException : Cannot access a disposed object.
Object name: 'IServiceProvider'

排查测试代码

修改测试仅初始化HttpClient而不发起请求,测试正常通过:

public class HealthCheckEndpointTests : IClassFixture<ItemsApplicationFactory>
{
    private readonly ItemsApplicationFactory _factory;

    public HealthCheckEndpointTests(ItemsApplicationFactory factory)
    {
        _factory = factory;
    }

    public async Task HealthCheck_Test()
    {
       // Arrange
       HttpClient httpClient = _factory.CreateClient();

       // Act 
       await Task.Run(() => Task.CompletedTask);

       // Assert
       Assert.True(true);
    }
}

疑问

为什么_factory.CreateClient();不会抛出异常,但调用httpClient.GetStringAsync("/health/live")会触发异常?该如何解决?


原因分析与解决方案

原因

你手动创建的serviceCollection.BuildServiceProvider()生成的服务提供器,在ConfigureServices方法执行完成后就会被自动释放。当健康检查接口被调用时,后端处理逻辑需要使用ItemsDbContext,而该DbContext绑定了已释放的服务提供器,因此触发ObjectDisposedException。

_factory.CreateClient()仅创建HttpClient实例,此时并未发起实际请求,也不会触及服务提供器,所以不会抛出异常;只有调用GetStringAsync发起请求后,后端处理流程中用到DbContext,才会触发对已释放服务提供器的访问。

解决方案

不要手动创建独立的服务提供器,直接复用WebApplicationFactory的现有服务容器即可。修改ItemsApplicationFactory的ConfigureServices方法:

public class ItemsApplicationFactory : WebApplicationFactory<Program>
{
    protected override void ConfigureWebHost(IWebHostBuilder builder)
    {
        builder.ConfigureKestrel(options => options.AllowSynchronousIO = true);

        builder.ConfigureServices(services =>
        {
            // 移除已存在的DbContext配置
            var dbContextDescriptor = services.SingleOrDefault(d => d.ServiceType == typeof(DbContextOptions<ItemsDbContext>));
            if (dbContextDescriptor != null)
                services.Remove(dbContextDescriptor);

            // 直接在现有服务集合中添加内存数据库支持
            services.AddEntityFrameworkInMemoryDatabase();
            
            services.AddDbContext<ItemsDbContext>(mariaDb =>
            {
                mariaDb.UseInMemoryDatabase("template");
                // 移除UseInternalServiceProvider调用,使用应用默认服务提供器
            });
        });
    }
}

修改后,DbContext会使用WebApplicationFactory管理的服务提供器,其生命周期与测试应用保持一致,不会提前被释放,请求处理时即可正常访问DbContext,异常问题也会解决。


内容的提问来源于stack exchange,提问作者TheSlowestDev

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 21:17:43