.NET 5 xUnit集成测试中如何替换服务注入的IDbContextFactory
问题核心原因
你当前的替换逻辑执行时机早于Startup类的服务注册逻辑:WebApplicationFactory中builder.ConfigureServices内的逻辑会先于Startup.ConfigureServices执行,你执行移除操作时服务集合中还没有注册原有的SQL Server版本IDbContextFactory<ApplicationDbContext>,相当于移除操作没有命中目标,后续Startup仍然会把原有的SQL Server工厂注册到服务集合中,最终运行时注入的还是原有工厂。
修复步骤
1. 替换服务配置方法
将builder.ConfigureServices改为builder.ConfigureTestServices,该方法内的逻辑会在Startup.ConfigureServices执行完成后运行,确保你能正确找到并移除原有注册。
2. 清理残留的DbContext配置
AddDbContextFactory注册时会同步注册DbContextOptions<ApplicationDbContext>服务,需要同时移除该注册避免原有SQL Server配置残留。
3. 可选优化:隔离测试数据库
为每个测试实例生成唯一的内存数据库名,避免不同测试用例之间的数据干扰。
修复后的完整代码示例
public class TestApplicationFactory<TStartup> : WebApplicationFactory<TStartup> where TStartup: class { protected override void ConfigureWebHost(IWebHostBuilder builder) { // 改用ConfigureTestServices确保在Startup注册服务后执行 builder.ConfigureTestServices(services => { // 移除旧的DbContextFactory注册 var contextFactory = services.SingleOrDefault( d => d.ServiceType == typeof(IDbContextFactory<ApplicationDbContext>) ); if (contextFactory != null) services.Remove(contextFactory); // 移除旧的DbContextOptions注册 var contextOptions = services.SingleOrDefault( d => d.ServiceType == typeof(DbContextOptions<ApplicationDbContext>) ); if (contextOptions != null) services.Remove(contextOptions); // 生成唯一数据库名实现测试隔离 var testDbName = $"InMemoryDbForTesting_{Guid.NewGuid()}"; services.AddDbContextFactory<ApplicationDbContext>( options => options.UseInMemoryDatabase(testDbName) ); var sp = services.BuildServiceProvider(); using var scope = sp.CreateScope(); var dbf = scope.ServiceProvider.GetRequiredService<IDbContextFactory<ApplicationDbContext>>(); var db = dbf.CreateDbContext(); db.Database.EnsureCreated(); DbSeed.InitializeDbForTests(db); // 用完后释放上下文 db.Dispose(); }); } }
内容的提问来源于stack exchange,提问作者Nathan
相关产品推荐
相关产品推荐

