使用Moq模拟ProjectDbContext时报Unsupported expression错误如何解决
问题原因及解决步骤
1. 两个报错的根本原因
- 第一个
Non-overridable members报错:Moq 只能对虚成员做Setup配置,默认实现的DbSet属性没有加virtual修饰,无法被重写代理。 - 第二个
Can not instantiate proxy of class报错:你的ProjectDbContext只有带DbContextOptions<ProjectDbContext>参数的构造函数,直接实例化Mock<ProjectDbContext>时没有传入对应构造参数,动态代理无法生成实例。
2. 具体修正代码
第一步:修正ProjectDbContext定义,给DbSet加virtual修饰
public class ProjectDbContext : DbContext { // 加virtual修饰支持Mock重写 public virtual DbSet<Item> Items { get; set; } public ProjectDbContext(DbContextOptions<ProjectDbContext> opt) : base(opt) {} }
第二步:修正测试类的Mock初始化逻辑
public class ItemTest { private readonly Mock<DbSet<Item>> _mockSet = new Mock<DbSet<Item>>(); private Mock<ProjectDbContext> _mockContext; public ItemTest() { // 构造Mock时传入DbContextOptions参数,匹配ProjectDbContext的构造函数要求 var options = new DbContextOptionsBuilder<ProjectDbContext>().Options; _mockContext = new Mock<ProjectDbContext>(options); _mockContext.Setup(m => m.Items).Returns(_mockSet.Object); } [Fact] public void GetById_ShouldReturnItemWhenItemExists() { // Arrange var itemId = Guid.NewGuid(); var testItem = new Item { Id = itemId }; // 配置Mock DbSet的IQueryable实现,支持LINQ查询操作 var data = new List<Item> { testItem }.AsQueryable(); _mockSet.As<IQueryable<Item>>().Setup(m => m.Provider).Returns(data.Provider); _mockSet.As<IQueryable<Item>>().Setup(m => m.Expression).Returns(data.Expression); _mockSet.As<IQueryable<Item>>().Setup(m => m.ElementType).Returns(data.ElementType); _mockSet.As<IQueryable<Item>>().Setup(m => m.GetEnumerator()).Returns(data.GetEnumerator()); // 初始化服务 var repository = new ItemsService(_mockContext.Object); // Act // 移除多余的CancellationToken参数,和IItemsService定义的GetById方法签名保持一致 var item = repository.GetById(itemId); // Assert Assert.Equal(itemId, item.Id); } }
3. 可选优化方案
如果不想给DbContext的所有DbSet都加virtual修饰,可以直接用EF Core自带的内存数据库做测试,不需要Mock DbContext,代码更简洁,也更贴合真实的数据库操作逻辑。
内容的提问来源于stack exchange,提问作者OhWelp
相关产品推荐
相关产品推荐

