如何在EF Core中对使用FromSqlInterpolated的存储过程方法进行单元测试?
如何Mock存储过程调用的单元测试
你的当前Mock思路不生效,因为待测试方法并没有调用DbContext上的GetDataFromStoredProcedure方法,而是直接通过MyModel.FromSqlInterpolated执行SQL语句,所以需要针对DbSet<MyModel>的行为进行Mock,以下是两种可行方案:
方案一:Mock DbSet的FromSqlInterpolated行为
这种方式属于纯单元测试,完全隔离数据库依赖:
确保DbContext暴露DbSet属性
首先在MyDBContext中添加对应实体的DbSet:public DbSet<MyModel> MyModels { get; set; }调整待测试方法
修改方法,从DbContext的DbSet调用FromSqlInterpolated:public async Task<List<MyModel>> GetDataFromStoredProcedure(int Id, CancellationToken cancellationToken) { return await _dbContext.MyModels.FromSqlInterpolated($"EXEC {MyStoredProcedure} {Id}").ToListAsync(cancellationToken); }编写单元测试代码
构建Mock DbSet并模拟FromSqlInterpolated的返回:// Arrange var mockData = await GetData(); var mockDbSet = new Mock<DbSet<MyModel>>(); // 将模拟数据转为IQueryable,模拟DbSet的查询能力 var queryableData = mockData.AsQueryable(); mockDbSet.As<IQueryable<MyModel>>().Setup(m => m.Provider).Returns(queryableData.Provider); mockDbSet.As<IQueryable<MyModel>>().Setup(m => m.Expression).Returns(queryableData.Expression); mockDbSet.As<IQueryable<MyModel>>().Setup(m => m.ElementType).Returns(queryableData.ElementType); mockDbSet.As<IQueryable<MyModel>>().Setup(m => m.GetEnumerator()).Returns(queryableData.GetEnumerator()); // Mock FromSqlInterpolated方法,返回我们的Mock DbSet mockDbSet.Setup(m => m.FromSqlInterpolated(It.IsAny<FormattableString>())) .Returns(mockDbSet.Object); // 将Mock DbSet注入到DbContext var mockDBContext = new Mock<MyDBContext>(options); mockDBContext.Setup(x => x.MyModels).Returns(mockDbSet.Object); // 实例化待测试服务 var service = new YourService(mockDBContext.Object); // Act var result = await service.GetDataFromStoredProcedure(1, CancellationToken.None); // Assert Assert.Equal(mockData.Count, result.Count); // 根据需求添加其他断言逻辑
方案二:使用内存数据库+拦截器模拟存储过程
这种方式更接近实际运行环境,适合轻量集成测试:
创建SQL命令拦截器
拦截存储过程调用,返回模拟数据:public class StoredProcedureInterceptor : DbCommandInterceptor { private readonly List<MyModel> _mockData; public StoredProcedureInterceptor(List<MyModel> mockData) { _mockData = mockData; } public override InterceptionResult<DbDataReader> ReaderExecuting( DbCommand command, CommandEventData eventData, InterceptionResult<DbDataReader> result) { // 判断是否是目标存储过程调用 if (command.CommandText.StartsWith($"EXEC {MyStoredProcedure}")) { // 构建模拟DataReader返回数据 var dataTable = new DataTable(); // 这里根据MyModel的属性添加对应列,示例假设Id和Name属性 dataTable.Columns.Add("Id", typeof(int)); dataTable.Columns.Add("Name", typeof(string)); foreach (var item in _mockData) { dataTable.Rows.Add(item.Id, item.Name); } var reader = dataTable.CreateDataReader(); return InterceptionResult<DbDataReader>.SuppressWithResult(reader); } return base.ReaderExecuting(command, eventData, result); } }编写测试代码
配置内存数据库并注入拦截器:// Arrange var mockData = await GetData(); var options = new DbContextOptionsBuilder<MyDBContext>() .UseInMemoryDatabase(databaseName: "TestDB") .AddInterceptors(new StoredProcedureInterceptor(mockData)) .Options; using var dbContext = new MyDBContext(options); var service = new YourService(dbContext); // Act var result = await service.GetDataFromStoredProcedure(1, CancellationToken.None); // Assert Assert.Equal(mockData.Count, result.Count); // 添加其他断言逻辑
内容的提问来源于stack exchange,提问作者user24640939
相关产品推荐
相关产品推荐

