You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET MSTest使用Moq模拟DbContext报错的问题修复问询

问题原因

你遇到的Non-overridable members异常,是因为Moq只能模拟可重写的成员(虚方法/属性、接口成员),而你的TaskPlannerDbContext里的Tasks属性是自动生成的非虚属性,Moq没法拦截它的调用。

修复方案

下面给你三种可行的修复方式,按需选择:

方案一:给DbSet属性添加virtual修饰符

直接修改TaskPlannerDbContext中的Tasks属性,加上virtual关键字,让它变成可重写的:

public class TaskPlannerDbContext : DbContext
{
    // 加上virtual修饰符
    public virtual DbSet<Task> Tasks { get; set; }
    // 其他DbSet和配置...
}

然后修正测试代码里的大小写错误(把c.tasks改成c.Tasks),同时补上loggerMock的初始化:

// 先初始化loggerMock
var loggerMock = new Mock<ILogger<TaskService>>();

// 修正大小写:c.Tasks而不是c.tasks
dbContextMock.Setup(c => c.Tasks).Returns(tasksDbSetMock.Object);

另外注意测试代码里的小错误:List<Task>里应该实例化Task而不是Expense:

var data = new List<Task>
{
    new Task { Id = 1, Name = "BBB"},
    new Task { Id = 2, Name = "ZZZ"},
    new Task { Id = 3, Name = "AAA"},
}.AsQueryable();

方案二:抽象DbContext接口(推荐,符合依赖倒置)

定义一个ITaskPlannerDbContext接口,包含Tasks属性:

public interface ITaskPlannerDbContext
{
    DbSet<Task> Tasks { get; set; }
    // 如果需要其他DbSet或方法,也可以加在这里
}

让TaskPlannerDbContext实现这个接口:

public class TaskPlannerDbContext : DbContext, ITaskPlannerDbContext
{
    public DbSet<Task> Tasks { get; set; }
    // 其他代码...
}

然后修改TaskService的构造函数,依赖接口而非具体的DbContext:

public class TaskService: ITask
{
    private readonly ITaskPlannerDbContext _dbContext;
    private readonly ILogger<TaskService> _logger;

    public TaskService(ITaskPlannerDbContext dbContext, ILogger<TaskService> logger)
    {
        _dbContext = dbContext;
        _logger = logger;
    }

    // 其他方法不变...
}

测试时直接mockITaskPlannerDbContext,就不会有非虚成员的问题了:

var dbContextMock = new Mock<ITaskPlannerDbContext>();
dbContextMock.Setup(c => c.Tasks).Returns(tasksDbSetMock.Object);

方案三:使用EF Core内存数据库(更贴近真实场景)

不需要mock DbSet,直接用EF Core的InMemory数据库来模拟真实的数据库操作,测试更可靠:

// 配置内存数据库
var options = new DbContextOptionsBuilder<TaskPlannerDbContext>()
    .UseInMemoryDatabase(databaseName: "TestTaskDb")
    .Options;

// 创建DbContext并填充测试数据
using var context = new TaskPlannerDbContext(options);
context.Tasks.AddRange(
    new Task { Id = 1, Name = "BBB"},
    new Task { Id = 2, Name = "ZZZ"},
    new Task { Id = 3, Name = "AAA"}
);
context.SaveChanges();

// 初始化loggerMock和service
var loggerMock = new Mock<ILogger<TaskService>>();
var service = new TaskService(context, loggerMock.Object);

// 执行测试
var tasks2 = service.GetAllTasks();
Assert.AreEqual(3, tasks2.Count());

这种方式不需要处理mock的各种细节,测试逻辑更接近实际运行情况。

额外提示

你的测试代码里还有两个小问题:

  • List<Task>里错误实例化了Expense对象,要改成Task
  • loggerMock没有初始化,需要先创建var loggerMock = new Mock<ILogger<TaskService>>();

内容的提问来源于stack exchange,提问作者michal98

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 22:55:42