首页 > 解决方案 > 如何使用 InMemory 数据库检查单元测试中正确添加的记录

问题描述

使用InMemory数据库我正在尝试编写一个单元测试,它将检查一个方法是否正在向表中添加一条记录。记录被添加到被测类中,但是当我尝试重新创建时,我遇到了上下文处理错误。设置此测试的正确方法是什么,以便在处理上下文后检查内存中的表?

单元测试

[Fact]
public void Test_AddContact_AddsSuccessfully()
{
    var contextFactoryMock = new Mock<IContextFactory>();
    contextFactoryMock.Setup(x => x.CreateContext()).Returns(CreateInMemoryContactContext());

    var classUnderTest = new AddContact(contextFactoryMock.Object); 

    var response = classUnderTest.Run(new UkContactUsDto());

    using (var ctx = contextFactoryMock.Object.CreateContext()) 
    {
        var items = ctx.ContactUs.ToList(); //<--- Exception happens here due to ctx being disposed
    }
}

创建 InMemory 方法

private ContactContext CreateInMemoryContactContext()
{
    var _inMemoryContext = new ContactContext(new DbContextOptionsBuilder<ContactContext>().UseInMemoryDatabase((Guid.NewGuid().ToString())).Options);
    
    return _inMemoryContext;
}

方法即时测试

try
{
    await using var ctx = _contextFactory.CreateContext();
    ctx.ContactUs.Add(contactUs);
    ctx.SaveChanges();
}
catch (Exception ex)
{
    log.LogInformation(ex,"An error occured during contact us insertion.");
    throw;
}

错误信息

无法访问已释放的上下文实例。此错误的一个常见原因是释放从依赖注入中解析的上下文实例,然后尝试在应用程序的其他地方使用相同的上下文实例。如果您在上下文实例上调用“Dispose”或将其包装在 using 语句中,则可能会发生这种情况。如果你使用依赖注入,你应该让依赖注入容器负责处理上下文实例。

标签: c#unit-testingentity-framework-core

解决方案


在 Mock 中,该方法CreateContext使用DbContext. Mock 为所有调用返回相同的实例CreateContext。您可以使用此测试进行检查:

var context = CreateInMemoryContactContext();
var contextFactoryMock = new Mock<IContextFactory>();
contextFactoryMock.Setup(x => x.CreateContext()).Returns(context);
Assert.ReferenceEquals(context, contextFactoryMock.Object.CreateContext());

在想AddContact配置上下文。当您重用上下文进行断言时,上下文将被释放。

你需要通过一个工厂Returns

var contextFactoryMock = new Mock<IContextFactory>();
contextFactoryMock.Setup(x => x.CreateContext()).Returns(CreateInMemoryContactContext);

为了在上下文之间保存数据,您需要在创建新的 DbContext 时重用相同的 DbOption。使用 xunit,对于每个测试,都会实例化一个测试类的实例。您可以将初始化部分放在构造函数中:

public class AddContactTest
{
    private DbOptions _dbOptions;

    public AddContactTest()
    {
        _options = new DbContextOptionsBuilder<ContactContext>().UseInMemoryDatabase((Guid.NewGuid().ToString())).Options
    }

    private ContactContext CreateInMemoryContactContext()
    {
        var _inMemoryContext = new ContactContext(_dbOptions);
        return _inMemoryContext;
    }

    [Fact]
    public void Test_AddContact_AddsSuccessfully()
    {
        var contextFactoryMock = new Mock<IContextFactory>();
        contextFactoryMock.Setup(x => x.CreateContext()).Returns(CreateInMemoryContactContext);

        var classUnderTest = new AddContact(contextFactoryMock.Object); 

        var response = classUnderTest.Run(new UkContactUsDto());

        using (var ctx = contextFactoryMock.Object.CreateContext()) 
        {
            var items = ctx.ContactUs.ToList();
        }
    }
}

推荐阅读