.NET中Mock仓储ToListAsync()失败,提示不支持扩展方法
问题解决:Mock仓储GetQueryable().ToListAsync()时的NotSupportedException错误
错误根源
Moq无法Mock静态扩展方法(比如EntityFrameworkQueryableExtensions.ToListAsync),你直接在Setup中链式调用GetQueryable().ToListAsync的写法,Moq无法识别这种表达式,因此抛出错误。
解决步骤
1. 删除错误的Setup语句
直接删掉这行无效且触发错误的代码:
_mockRepo.Setup(x => x.GetQueryable<UserCompany>().ToListAsync(It.IsAny<CancellationToken>())).ReturnsAsync(userCompanies);
2. 让GetQueryable返回的IQueryable支持异步操作
普通List.AsQueryable()是Linq to Objects实现,不支持EF的异步扩展方法。你可以选以下两种方式解决:
方式一:使用EF Core内存数据库(推荐,贴近真实场景)
放弃Mock仓储,用InMemory数据库模拟真实查询逻辑,完全避免Mock Linq操作的问题:
// 创建内存数据库配置 var options = new DbContextOptionsBuilder<YourDbContext>() .UseInMemoryDatabase(databaseName: "TestLoginDb") .Options; // 插入测试数据 using (var context = new YourDbContext(options)) { context.UserCompanies.AddRange(userCompanies); await context.SaveChangesAsync(); } // 实例化真实仓储,传入内存上下文 var realRepo = new YourRepo(new YourDbContext(options)); // 将真实仓储注入userService,替换原Mock仓储 var userService = new UserService(_userManager.Object, realRepo);
方式二:使用异步Queryable扩展(必须Mock仓储时用)
安装NuGet包System.Linq.Async,将普通Queryable转换为支持异步的类型:
// 先安装NuGet包:System.Linq.Async var userCompanies = new List<UserCompany> { new UserCompany { Id = 1, CompanyId = 1, UserId = 1 } }; // 转换为支持异步的IQueryable var queryableUserCompanies = userCompanies.AsQueryable().AsAsyncQueryable(); _mockRepo.Setup(x => x.GetQueryable<UserCompany>()).Returns(queryableUserCompanies);
3. 验证Linq逻辑(可选)
如果需要验证仓储调用及Linq条件是否正确,可捕获返回的Queryable进行检查:
IQueryable<UserCompany> capturedQuery = null; _mockRepo.Setup(x => x.GetQueryable<UserCompany>()) .Callback(() => capturedQuery = userCompanies.AsQueryable().AsAsyncQueryable()) .Returns(userCompanies.AsQueryable().AsAsyncQueryable()); // 执行测试 var result = await userService.LoginAsync(new UserLoginDTO { Email = email, Password = password }); // 验证Linq条件是否符合预期 var filteredIds = capturedQuery.AsNoTracking() .Where(c => c.UserId == 1) .Select(c => c.CompanyId) .ToList(); Assert.Equal(new List<int> {1}, filteredIds);
关于你自定义的扩展方法
你写的ToListAsync扩展未生效,是因为业务代码引用的是Microsoft.EntityFrameworkCore命名空间下的官方扩展,其优先级高于你自定义的扩展,测试时仍会调用官方静态方法,而静态方法无法被Mock。
内容的提问来源于stack exchange,提问作者Nour Mawla
相关产品推荐
相关产品推荐

