XUnit并行测试引发EF Core与Sqlite数据异常问题
问题原因
你的DataContextFactory复用了同一个Sqlite连接对象,所有通过该工厂创建的DataContext都会操作同一个内存数据库实例。即使禁用测试并行,只要多个上下文共享连接,就会出现不同测试(或同一测试内的多个上下文)互相污染数据的情况——比如测试方法内同时使用构造函数创建的DataContext和GetContextAndRepository返回的DataContext,两者共享连接、操作同一个数据库,最终导致数据混乱。
解决方案
1. 重构DataContextFactory,确保每个上下文使用独立连接
修改工厂类,让每次创建上下文时生成新的Sqlite连接,使每个上下文对应独立的内存数据库,彻底隔离测试数据:
public class DataContextFactory : IDisposable { private readonly List<SqliteConnection> _activeConnections = []; public DataContext Create() { // 每次创建全新的内存数据库连接 var connection = new SqliteConnection("Filename=:memory:"); connection.Open(); // 内存数据库必须保持连接打开,否则会被销毁 _activeConnections.Add(connection); var options = new DbContextOptionsBuilder<DataContext>() .UseSqlite(connection) .Options; var context = new DataContext(options); context.Database.EnsureCreated(); // 新内存库为空,直接初始化表结构即可 return context; } public void Dispose() { // 清理所有创建的连接 foreach (var conn in _activeConnections) { conn.Dispose(); } GC.SuppressFinalize(this); } }
2. 调整测试基类,统一上下文管理
确保测试方法内的所有数据库操作都基于同一个上下文,避免同一测试内多个上下文共享数据库:
public abstract class CrudRepositoryTests<TTestEntity> : IDisposable where TTestEntity : BaseEntity { private readonly DataContextFactory _factory = new(); private readonly List<DataContext> _usedContexts = []; // 封装上下文和仓库的创建,确保每次返回独立实例 protected (DataContext Context, IRepository<TTestEntity> Repository) GetTestDependencies() { var context = _factory.Create(); _usedContexts.Add(context); var repository = CreateRepository(context); // 子类需实现仓库创建逻辑 return (context, repository); } protected abstract IRepository<TTestEntity> CreateRepository(DataContext context); protected abstract ICollection<TTestEntity> GetEntitiesToAdd(); public void Dispose() { // 清理所有使用过的上下文 foreach (var ctx in _usedContexts) { ctx.Dispose(); } _factory.Dispose(); GC.SuppressFinalize(this); } [Fact] public virtual async Task GetAllAsync_ShouldReturnAllEntities() { // Arrange var (context, repository) = GetTestDependencies(); var testEntities = await SetTestEntitiesAsync(context); // Act var result = await repository.GetAllAsync(); // Assert Assert.Equal(testEntities.Count, result.Count()); } protected async Task<ICollection<TTestEntity>> SetTestEntitiesAsync(DataContext context) { var existing = await context.Set<TTestEntity>().ToListAsync(); if (existing.Count > 0) { return existing; } var newEntities = GetEntitiesToAdd(); context.Set<TTestEntity>().AddRange(newEntities); await context.SaveChangesAsync(); return newEntities; } }
3. 验证测试隔离
- 移除测试类构造函数中提前创建的
DataContext,避免同一测试内存在多个上下文实例。 - 确保所有测试操作都通过
GetTestDependencies获取上下文和仓库,保证每个测试的数据库环境完全独立。
内容的提问来源于stack exchange,提问作者Denis
相关产品推荐
相关产品推荐

