.NET Core 3.1 Web API测试:Mock DbContext及一对多关系问题
解决Moq.MockException:Add()未被调用的问题
我之前也踩过类似的坑,大概率是你的Mock设置和业务代码的实际调用没对上,或者DbContext/DbSet的Mock方式有问题。咱们一步步来排查:
1. 先确认业务代码的Add调用方式
首先看你的CreateSchoolCommand里,是用的哪种Add方法?
- 如果是
_dbContext.Add<School>(newSchool),那你的Mock要针对DbContext.Add<T>这个泛型方法 - 如果是
_dbContext.Schools.Add(newSchool),那你需要Mock的是DbSet<School>.Add,而不是DbContext的泛型Add
这两种是不同的重载,Moq会严格匹配调用签名,Setup和实际调用的方法不一致,就会出现“未执行调用”的错误。
2. 正确Mock DbContext和DbSet
EF Core的DbContext依赖DbSet的实例,不能只Mock DbContext而忽略DbSet的设置。这里给你一个标准的Mock方式:
第一步:Mock DbSet
// 创建DbSet的Mock,同时支持IQueryable(如果业务代码有查询需求的话) var schoolDbSetMock = new Mock<DbSet<School>>(); schoolDbSetMock.As<IQueryable<School>>() .Setup(m => m.Provider) .Returns(Enumerable.Empty<School>().AsQueryable().Provider); schoolDbSetMock.As<IQueryable<School>>() .Setup(m => m.Expression) .Returns(Enumerable.Empty<School>().AsQueryable().Expression); schoolDbSetMock.As<IQueryable<School>>() .Setup(m => m.ElementType) .Returns(Enumerable.Empty<School>().AsQueryable().ElementType); schoolDbSetMock.As<IQueryable<School>>() .Setup(m => m.GetEnumerator()) .Returns(Enumerable.Empty<School>().AsQueryable().GetEnumerator());
第二步:关联DbSet到DbContext的Mock
var dbContextMock = new Mock<YourDbContext>(); // 关联Schools属性到刚才Mock的DbSet dbContextMock.Setup(m => m.Schools).Returns(schoolDbSetMock.Object); // 如果业务代码用的是_dbContext.Add<School>(...),还要Setup这个泛型方法 dbContextMock.Setup(m => m.Add(It.IsAny<School>())) .Callback<School>(school => schoolDbSetMock.Object.Add(school));
3. 确保测试中注入的是Mock的DbContext
如果你用AutoFixture来创建CreateSchoolCommand实例,一定要把Mock的DbContext注入到Fixture中,否则AutoFixture会自动创建一个真实的DbContext(这肯定不是你想要的):
var fixture = new Fixture(); // 把Mock的DbContext实例注入到Fixture fixture.Inject(dbContextMock.Object); // 然后用Fixture创建Command实例 var createSchoolCommand = fixture.Create<CreateSchoolCommand>();
4. 验证调用时要匹配实际的方法
最后,验证的时候要和业务代码的调用方式一致:
- 如果业务代码用
_dbContext.Add<School>(...),验证:
dbContextMock.Verify(m => m.Add(It.IsAny<School>()), Times.Once);
- 如果业务代码用
_dbContext.Schools.Add(...),验证:
schoolDbSetMock.Verify(m => m.Add(It.IsAny<School>()), Times.Once);
常见坑点提醒
- 不要忽略DbSet的IQueryable设置:如果你的业务代码中有查询Schools的逻辑,不设置IQueryable的话会抛出异常
- 检查是否有异步/同步混淆:如果业务代码用的是
AddAsync,那你要Setup对应的异步方法 - 确保DbContext的Mock是Loose行为(Moq默认是Loose,除非你特意设了Strict),Strict模式下未Setup的方法会抛出异常
按照上面的步骤调整后,应该就能解决这个MockException问题了。
内容的提问来源于stack exchange,提问作者K.Z
相关产品推荐
相关产品推荐

