当前位置: 首页 > 知识库问答 >
问题:

如何使用InMemory数据库检查单元测试中正确添加的记录

陶胤运
2023-03-14

使用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;
}

方法im测试

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语句中,则可能会出现这种情况。如果您正在使用依赖项注入,则应该让依赖项注入容器负责处理上下文实例。

共有2个答案

鲁财
2023-03-14

在模拟中,createContext方法是用dbcontext的实例模拟的。对于所有对createContext的调用,模拟返回相同的实例。您可以使用以下测试进行检查:

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

在thinkaddcontact中处理上下文。当您重用上下文来执行断言时,上下文将被释放。

您需要在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();
        }
    }
}
严俊彦
2023-03-14

您正在处理正在测试的方法中的上下文,这样您就不能再使用它了。解决这一问题的方法之一是创建一个新上下文,但您需要手动跟踪InMemoryDatabaseRoot实例。例如:

private static readonly _memoryDatabaseRoot InMemoryDatabaseRoot = new InMemoryDatabaseRoot();

private ContactContext CreateInMemoryContactContext()
{
    var _inMemoryContext = new ContactContext(new DbContextOptionsBuilder<ContactContext>()
        .UseInMemoryDatabase("my-database", _memoryDatabaseRoot).Options);
    
    return _inMemoryContext;
}

现在,在测试中,您可以调用CreateInMemoryContactContext来获取使用相同内存中数据库实例的新上下文

注意:如果要在多个测试中使用根,您可能不希望它是静态的。

 类似资料:
  • 问题内容: 我希望在编写用于测试某些数据库条目的单元测试中获得一些建议。 如果找不到记录,则我正在测试的功能会将数据库作为种子。 我似乎不太了解如何进行len测试。我正在使用测试数据库,因此我可以在任何时候都对其进行核对,因此,如果我只需要在函数上强制使用一个空的数据库,就没有问题了。 该函数本身可以正常工作,我只想确保已覆盖该函数。 任何建议都很好。 谢谢! 问题答案: 确实取决于您,有很多方法

  • 我是spring的新手,目前我正在尝试为我的项目执行单元测试。我已经用hibernate配置了spring,现在我想检查创建的类的方法是否工作。例如,假设我有: 如何正确测试这些方法?最好的选择是什么?我习惯在JUnit测试中使用assertTrue和assertFalse,但我担心这在我的情况下不是一个好的选择。谢谢你。

  • 问题内容: 我做了一个简单的测试用例: 并且我希望测试能够通过,但是我遇到了异常: 我以为assertRaises将通过,因为将引发TypeError异常? 问题答案: 如果您使用的是python2.7或更高版本,则可以将assertRaises用作上下文管理器并执行以下操作: 如果您正在使用python2.6,那么到目前为止,还有另一种方法是使用unittest2,这是unittest新功能到p

  • 当你想要使用接近真实数据库的东西来测试组件,同时又不想造成实际数据库输入输出的操作开销时,内存(InMemory)提供程序会很有用。 提示 你可以在 GitHub 上查阅当前文章涉及的代码样例。 InMemory 不是关系数据库 EF Core 数据库提供程序并非就是关系数据库。InMemory 是设计用于测试的通用数据库,而不是模拟关系数据库。 与此相关的样例包括: InMemory将允许您保存

  • 我正在尝试使用测试驱动设计方法编写一个应用程序--我对单元测试很陌生,所以我只是想知道测试正确输入和异常的正确方法是什么。 我有一个用于加载配置文件的类: null 另外,这3个测试是否都有try{}catch(){}语句?在第一个测试中,正确性是隐含的,在第二个和第三个测试中,我无论如何都在检查异常,所以异常对测试没有影响。

  • 当管理员在结束视图中按下“conlude”按钮时,我希望程序遍历数据库中的每条记录,并检查是否正好有2条记录(不多不少)将候选设置为true。(Rest会是假的)。我以为我可以通过计数循环来实现这一点,该循环遍历每个记录并检查候选是否设置为true或false,但是我如何在ASP.NET MVC中实现这一点?? 谢谢你抽出时间 存储过程 行动/方法