如何在NUnit单元测试中Mock EF 7.0的ExecuteUpdateAsync方法
Mock EF 7.0 ExecuteUpdateAsync 方法的解决方案
首先明确核心问题:ExecuteUpdateAsync是Microsoft.EntityFrameworkCore.RelationalQueryableExtensions中的扩展方法,并非DbContext或DbSet<T>的实例方法,因此无法直接像SaveChangesAsync那样通过Mock DbContext来配置,需要Mock其底层依赖的IQueryable<T>的IQueryProvider来实现。
具体配置步骤
假设你的实体类型为User,DbContext中对应的DbSet为Users,以下是完整的Mock配置代码:
1. 准备测试数据与Mock DbSet
// 构造测试用的实体集合 var testUsers = new List<User> { new User { Id = 1, IsActive = true }, new User { Id = 2, IsActive = true } }.AsQueryable(); // Mock DbSet<User> var mockUserDbSet = new Mock<DbSet<User>>(); // 将DbSet关联到测试数据的IQueryable实现 mockUserDbSet.As<IQueryable<User>>().Setup(m => m.Provider).Returns(mockQueryProvider.Object); mockUserDbSet.As<IQueryable<User>>().Setup(m => m.Expression).Returns(testUsers.Expression); mockUserDbSet.As<IQueryable<User>>().Setup(m => m.ElementType).Returns(testUsers.ElementType); mockUserDbSet.As<IQueryable<User>>().Setup(m => m.GetEnumerator()).Returns(testUsers.GetEnumerator());
2. Mock IQueryProvider 处理异步执行
var mockQueryProvider = new Mock<IQueryProvider>(); // 配置ExecuteAsync,对应ExecuteUpdateAsync的异步执行逻辑,返回预期受影响行数 mockQueryProvider .Setup(provider => provider.ExecuteAsync<int>( It.IsAny<Expression>(), It.IsAny<CancellationToken>())) .ReturnsAsync(2); // 这里返回你预期的受影响行数,比如2
3. 关联DbSet到DbContext并保留已有配置
var mockDbContext = new Mock<YourDbContext>(); // 将Mock的DbSet注入到DbContext mockDbContext.Setup(ctx => ctx.Users).Returns(mockUserDbSet.Object); // 保留已有的SaveChangesAsync配置 mockDbContext.Setup(s => s.SaveChangesAsync(It.IsAny<CancellationToken>())).ReturnsAsync(1);
报错原因说明
你遇到的System.InvalidOperationException: There is no method 'ExecuteUpdate' on type 'Microsoft.EntityFrameworkCore.RelationalQueryableExtensions'异常,是因为扩展方法ExecuteUpdateAsync依赖底层IQueryProvider的实现,当未Mock该Provider时,扩展方法无法找到有效的执行逻辑,从而抛出异常。
扩展说明
如果需要根据不同的SetProperty表达式返回不同结果,可以调整Setup中的It.Is<Expression>()条件,匹配具体的更新表达式。例如:
mockQueryProvider .Setup(provider => provider.ExecuteAsync<int>( It.Is<Expression>(expr => expr.ToString().Contains("IsActive = False")), It.IsAny<CancellationToken>())) .ReturnsAsync(2);
内容的提问来源于stack exchange,提问作者coder11 b
相关产品推荐
相关产品推荐

