Asp.net core 使用Moq和xUnit模拟EF核心数据库上下文?

Asp.net core 使用Moq和xUnit模拟EF核心数据库上下文?,asp.net-core,moq,xunit,Asp.net Core,Moq,Xunit,我想知道当我使用DI向控制器提供数据库上下文时,如何使用Moq模拟EF核心的DbContext,如下所示: 公共类注册表控制器:控制器 { 私有AppDbContext上下文; 公共注册表控制器(AppDbContext AppDbContext) { context=appDbContext; } public IActionResult Create() { 返回视图(); } [HttpPost] 公共异步任务创建(寄存器) { if(ModelState.IsValid) { 添加(寄存

我想知道当我使用DI向控制器提供数据库上下文时,如何使用Moq模拟EF核心的
DbContext
,如下所示:

公共类注册表控制器:控制器
{
私有AppDbContext上下文;
公共注册表控制器(AppDbContext AppDbContext)
{
context=appDbContext;
}
public IActionResult Create()
{
返回视图();
}
[HttpPost]
公共异步任务创建(寄存器)
{
if(ModelState.IsValid)
{
添加(寄存器);
wait context.saveChangesSync();
返回重定向到操作(“读取”);
}
其他的
返回视图();
}
}
这里
AppDbContext
是EF核心的数据库上下文

我想为
Create
操作编写测试用例。我尝试了以下代码:

[事实]
公共异步任务测试\创建\发布\有效模型状态()
{
//安排
var r=新寄存器()
{
Id=4,
Name=“测试四”,
年龄=59
};
var mockRepo=new Mock();
mockRepo.Setup(repo=>repo.CreateAsync(It.IsAny()))
.Returns(Task.CompletedTask)
.可验证();
var控制器=新的注册控制器(mockRepo.Object);
//表演
var结果=等待控制器。创建(r);
//断言
var redirectToActionResult=Assert.IsType(结果);
Assert.Null(redirectToActionResult.ControllerName);
相等(“读取”,重定向到ActionResult.ActionName);
mockRepo.Verify();
}
这里的主要问题是我无法做到:

var mockRepo=new Mock();
我想采用这种方法,因为我已经在内存数据库中添加了

请注意,我知道还有另一种使用存储库模式进行测试的方法。可以通过将创建操作更改为:

公共类注册表控制器:控制器
{
私人地理位置背景;
公共注册控制器(IRegisterRepository appDbContext)
{ 
context=appDbContext;
}
public IActionResult Create()
{
返回视图();
}
[HttpPost]
公共异步任务创建(寄存器)
{
if(ModelState.IsValid)
{
wait context.CreateAsync(寄存器);
返回重定向到操作(“读取”);
}
其他的
返回视图();
}
}
其中接口
IRegisterRepository
RegisterRepository.cs
代码为:

公共接口IRegisterRepository
{     
任务CreateAsync(寄存器);
}
公共类注册存储:IRegisterRepository
{
私有只读AppDbContext上下文;
公共注册表存储库(AppDbContext)
{
context=dbContext;
}
公共任务CreateAsync(寄存器)
{
context.Register.Add(Register);
返回context.saveChangesSync();
}
}
将其添加为服务的startup.cs代码为:

public void配置服务(IServiceCollection服务)
{
services.AddDbContext(optionsBuilder=>optionsBuilder.UseInMemoryDatabase(“InMemoryDb”);
services.addScope();
services.AddControllersWithViews();
}
我不想采用这种方法(存储库模式)。我想直接伪造数据库上下文,因为我想直接在创建操作中插入记录,因为控制器在其构造函数中获取数据库上下文对象


那么,如何在这里的测试方法中使用moq编写假数据库上下文呢?

有几个非常有用的库支持您所寻找的内容。
以下是我最喜欢的两个:

EntityFrameworkCore3Mock

唯一的先决条件是必须将
DbSet
定义为
virtual

公共类AppDbContext:DbContext
{
公共虚拟数据库集实体{get;set;}
}
那么嘲弄就这么简单了:

var initialEntities=new[]
{
新实体{…},
新实体{…},
};
var dbContextMock=新的dbContextMock

这里唯一的区别是如何使用数据初始化表:

var initialEntities=new[]
{
新实体{…},
新实体{…},
};
var dbContextMock=Create.mockedbContextfor();
dbContextMock.Set().AddRange(initialEntities);
dbContextMock.SaveChanges();

不要模拟DbContext,因为使用模拟DbContext的测试不会为开发人员提供高质量的反馈

Mocked DbContext将只验证调用了某些方法,这将使代码维护(重构)变成一场噩梦

而是使用内存中提供程序或具有“内存中”功能的Sqlite提供程序


如果您不能
new Mock()
并且不想将
AppDbContext
抽象出来,那么为什么不直接启动一个实际的
AppDbContext
?您考虑过使用InMemoryDatabase包来测试这个吗?@mxhow。请写一个答案,以便我能理解。@ShafiqJetha是的,我正在使用内存数据库。如果您已经在使用内存数据库,那么您应该只需要使用指向此数据库的生成器实例化一个新的数据库上下文。然后对数据进行种子设定并运行测试。有没有一种方法可以在不使用这些库的情况下处理这些数据。@yogihosting为什么要这样做?你不需要重新发明轮子。只需使用使您的生活更轻松的工具。但我希望使用数据库上下文直接注入控制器,然后控制器操作将直接使用数据库上下文执行CRUD操作。所以在这种情况下,我想编写测试方法。那么,如何使用moq实现这一点呢?您不需要使用moq,您可以注入实际的DbContext,但可以向内存提供程序注册它。谢谢。我明白我不能使用最小起订量