繁体   English   中英

Mocking 使用 Moq 和 xUnit 的 EF 核心数据库上下文?

[英]Mocking EF Core Database Context using Moq and xUnit?

当我使用 DI 向 controller 提供我的数据库上下文时,我想知道如何将 Moq 用于 mocking 我的 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();
}

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

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

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

模拟的 DbContext 将仅验证调用了某些方法,这会将代码维护(重构)变成一场噩梦。

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

EF Core 内存中数据库提供程序

使用 SQLite 测试 EF Core 应用程序

替代方法是针对实际数据库进行测试 - 此类测试将使应用程序在“生产”环境中正常工作更有信心。

有几个非常有用的库确实支持您正在寻找的东西。
这是我最喜欢的两个:

EntityFrameworkCore3Mock

Github 回购

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

public class AppDbContext: DbContext
{
    public virtual DbSet<Entity> Entities { get; set; }
}

那么 mocking 就这么简单:

var initialEntities = new[]
{
    new Entity { ... },
    new Entity { ... },
};

var dbContextMock = new DbContextMock<AppDbContext>(DummyOptions);
var usersDbSetMock = dbContextMock.CreateDbSetMock(x => x.Entities, initialEntities);

EntityFrameworkCore.测试

Github 回购

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

var initialEntities = new[]
{
    new Entity { ... },
    new Entity { ... },
};


var dbContextMock = Create.MockedDbContextFor<AppDbContext>();
dbContextMock.Set<Entity>().AddRange(initialEntities);
dbContextMock.SaveChanges();

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM