如何用内存数据库触发Repository的Catch代码块测试
解决EF Core内存数据库测试Repository异常捕获逻辑的方法
方法1:利用EF Core拦截器强制抛出异常
EF Core的拦截器机制可以在查询执行生命周期中注入自定义逻辑,通过实现IQueryInterceptor拦截查询操作并抛出异常,就能触发Repository的Catch块:
- 定义自定义拦截器,在查询执行阶段抛出指定异常:
public class ThrowExceptionQueryInterceptor : IQueryInterceptor { public ValueTask<QueryExecutionStartingEventData> QueryExecutionStartingAsync( QueryExecutionStartingEventData eventData, CancellationToken cancellationToken = default) { // 可根据查询目标实体或语句特征精准触发异常 if (eventData.Query.ToString().Contains("SomeDbSetProperty")) { throw new DbException("模拟数据库查询异常"); } return new ValueTask<QueryExecutionStartingEventData>(eventData); } }
- 创建内存数据库DbContext时添加该拦截器:
var options = new DbContextOptionsBuilder<YourDbContext>() .UseInMemoryDatabase(databaseName: "TestDb") .AddInterceptors(new ThrowExceptionQueryInterceptor()) .Options; var dbContext = new YourDbContext(options); var repository = new YourRepository(dbContext, loggerMock.Object);
当Repository调用_dbContext.SomeDbSetProperty.ToListAsync()时,拦截器会触发并抛出异常,直接进入Catch块。
方法2:替换DbSet的异步枚举器为抛异常实现
手动构造会抛出异常的IAsyncEnumerable,替换DbSet的异步枚举行为,以此触发ToListAsync()的异常:
- 实现自定义异步枚举器和枚举集合:
public class ThrowingAsyncEnumerator<T> : IAsyncEnumerator<T> { public T Current => throw new InvalidOperationException("模拟异步枚举异常"); public ValueTask DisposeAsync() => ValueTask.CompletedTask; public ValueTask<bool> MoveNextAsync() { throw new DbException("模拟数据库查询异常"); } } public class ThrowingAsyncEnumerable<T> : IAsyncEnumerable<T> { public IAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancellationToken = default) { return new ThrowingAsyncEnumerator<T>(); } }
- 在测试中替换DbSet的异步枚举逻辑:
var options = new DbContextOptionsBuilder<YourDbContext>() .UseInMemoryDatabase(databaseName: "TestDb") .Options; var dbContext = new YourDbContext(options); // 替换目标DbSet的异步枚举行为 dbContext.SomeDbSetProperty = dbContext.SomeDbSetProperty .AsQueryable() .AsAsyncEnumerable(new ThrowingAsyncEnumerable<YourEntity>()); var repository = new YourRepository(dbContext, loggerMock.Object);
调用ToListAsync()时会使用自定义枚举器,直接抛出异常触发Catch逻辑。
方法3:利用内存数据库约束冲突触发异常(适用于写操作)
如果需要测试Repository写操作的异常逻辑(比如AddAsync+SaveChangesAsync),可以通过构造数据约束冲突触发异常:
var options = new DbContextOptionsBuilder<YourDbContext>() .UseInMemoryDatabase(databaseName: "TestDb") .Options; var dbContext = new YourDbContext(options); // 先添加一个主键为1的实体 dbContext.SomeDbSetProperty.Add(new YourEntity { Id = 1 }); await dbContext.SaveChangesAsync(); // 再添加相同主键的实体,触发内存数据库的主键约束异常 dbContext.SomeDbSetProperty.Add(new YourEntity { Id = 1 }); var repository = new YourRepository(dbContext, loggerMock.Object); // 调用Add方法后会触发异常 await Assert.ThrowsAsync<DbUpdateException>(() => repository.AddAsync(duplicateEntity));
此方法仅适用于写操作场景,读操作还是前两种方法更直接。
内容的提问来源于stack exchange,提问作者Kyle
相关产品推荐
相关产品推荐

