使用 Moq 和 xUnit 模拟 EF Core 数据库上下文?

如何解决使用 Moq 和 xUnit 模拟 EF Core 数据库上下文?

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

public class RegisterController : Controller
{
    private AppDbContext context;
    public RegisterController(AppDbContext appDbContext)
    {
        context = appDbContext;
    }

    public IActionResult Create()
    {
        return View();
    }

    [HttpPost]
    public async Task<IActionResult> Create(Register register)
    {
        if (ModelState.IsValid)
        {
            context.Add(register);
            await context.SaveChangesAsync();

            return RedirectToAction("Read");
        }
        else
            return View();
    }
}

这里的 AppDbContext 是我的 EF 核心数据库上下文。

我想为 Create 操作编写测试用例。我试过下面的代码

[Fact]
public async Task Test_Create_POST_ValidModelState()
{
    // Arrange
    var r = new Register()
    {
        Id = 4,Name = "Test Four",Age = 59
    };

    var mockRepo = new Mock<AppDbContext>();
    mockRepo.Setup(repo => repo.CreateAsync(It.IsAny<Register>()))
        .Returns(Task.CompletedTask)
        .Verifiable();
    var controller = new RegisterController(mockRepo.Object);

    // Act
    var result = await controller.Create(r);

    // Assert
    var redirectToActionResult = Assert.IsType<RedirectToActionResult>(result);
    Assert.Null(redirectToActionResult.ControllerName);
    Assert.Equal("Read",redirectToActionResult.ActionName);
    mockRepo.Verify();
}

这里的主要问题是我做不到:

var mockRepo = new Mock<AppDbContext>();

我想采用这种方法,因为我已经添加了内存数据库

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

public class RegisterController : Controller
{
    private IRegisterRepository context;
    public RegisterController(IRegisterRepository appDbContext)
    { 
        context = appDbContext;
    }

    public IActionResult Create()
    {
        return View();
    }

    [HttpPost]
    public async Task<IActionResult> Create(Register register)
    {
        if (ModelState.IsValid)
        {
            await context.CreateAsync(register);
            return RedirectToAction("Read");
        }
        else
            return View();
    }
}

接口 IRegisterRepositoryRegisterRepository.cs 代码在哪里:

public interface IRegisterRepository
{     
    Task CreateAsync(Register register);
}

public class RegisterRepository : IRegisterRepository
{
    private readonly AppDbContext context;

    public RegisterRepository(AppDbContext dbContext)
    {
        context = dbContext;
    }

    public Task CreateAsync(Register register)
    {
        context.Register.Add(register);
        return context.SaveChangesAsync();
    }
}

以及将其添加为服务的 startup.cs 代码是:

public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContext<AppDbContext>(optionsBuilder => optionsBuilder.UseInMemoryDatabase("InMemoryDb"));
    services.AddScoped<IRegisterRepository,RegisterRepository>();
    services.AddControllersWithViews();
}

我不想遵循这种方法(存储库模式)。我想直接伪造数据库上下文,因为我想直接在创建操作中插入记录,因为控制器正在其构造函数获取数据库上下文对象。

那么如何在此测试方法中使用最小起订量编写假数据库上下文?

解决方法

不要模拟 DbContext,因为模拟 dbContext 的测试不会为开发人员提供良好的质量反馈。

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

改为使用具有“内存中”功能的内存中提供程序或 Sqlite 提供程序。

EF Core In-Memory Database Provider

Using SQLite to test an EF Core application

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?
Java在半透明框架/面板/组件上重新绘画。
Java“ Class.forName()”和“ Class.forName()。newInstance()”之间有什么区别?
在此环境中不提供编译器。也许是在JRE而不是JDK上运行?
Java用相同的方法在一个类中实现两个接口。哪种接口方法被覆盖?
Java 什么是Runtime.getRuntime()。totalMemory()和freeMemory()?
java.library.path中的java.lang.UnsatisfiedLinkError否*****。dll
JavaFX“位置是必需的。” 即使在同一包装中
Java 导入两个具有相同名称的类。怎么处理?
Java 是否应该在HttpServletResponse.getOutputStream()/。getWriter()上调用.close()?
Java RegEx元字符(。)和普通点?