具有内存中 EF 核心的 Moq IDBContextFactory

如何解决具有内存中 EF 核心的 Moq IDBContextFactory

我正在测试一个使用 DbContext 的类。该类注入了一个 IDbContextFactory,然后用于获取 DbContext:

protected readonly IDbContextFactory<SomeDbContext> ContextFactory;

public Repository(IDbContextFactory<SomeDbContext> contextFactory)
{
    ContextFactory = contextFactory;
}

public List<T> Get()
{
    using var db = ContextFactory.CreateDbContext();
    return db.Set<T>().ToList();
}

我可以为一次测试进行设置,但每次我想使用上下文时都必须调用 Mock<DbContextFactory>.Setup(f => f.CreateDbContext()) 方法

这是一个例子:

var mockDbFactory = new Mock<IDbContextFactory<SomeDbContext>>();
mockDbFactory.Setup(f => f.CreateDbContext())
    .Returns(new SomeDbContext(new DbContextOptionsBuilder<SomeDbContext>()
        .UseInMemoryDatabase("InMemoryTest")
        .Options));
var repository = new Repository<SomeEntity>(mockDbFactory.Object);

// non-existent id
Assert.IsNull(repository.Get(-1));

这很好用。但是,如果我添加一个回购调用(如 Assert.DoesNotthrow(() => repository.Get(1);),我得到

System.ObjectdisposedException: Cannot access a disposed context instance.

如果我再次调用 Mock<T>.Setup(),一切正常

var mockDbFactory = new Mock<IDbContextFactory<SomeDbContext>>();
mockDbFactory.Setup(f => f.CreateDbContext())
    .Returns(new SomeDbContext(new DbContextOptionsBuilder<SomeDbContext>()
        .UseInMemoryDatabase("InMemoryTest")
        .Options));
var repository = new Repository<SomeEntity>(mockDbFactory.Object);

// non-existent id
Assert.IsNull(repository.Get(-1));

mockDbFactory.Setup(f => f.CreateDbContext())
    .Returns(new SomeDbContext(new DbContextOptionsBuilder<SomeDbContext>()
        .UseInMemoryDatabase("InMemoryTest")
        .Options));

// pass
Assert.DoesNotthrow(() => repository.Get(1));

这是 Get(int id) 方法

public T Get(int id)
{
    using var db = ContextFactory.CreateDbContext();
    return db.Set<T>().Find(id);
}

据我所知,Mock 已准备好返回

new SomeDbContext(new DbContextOptionsBuilder<SomeDbContext>()
                .UseInMemoryDatabase("InMemoryTest")
                .Options)

每次调用 .CreateDbContext() 时。对我来说,这意味着它每次都应该返回一个新的上下文实例,而不是已经处理过的实例。但是,它似乎返回了相同的已处理实例。

解决方法

mockDbFactory.Setup(f => f.CreateDbContext())
    .Returns(new SomeDbContext(new DbContextOptionsBuilder<SomeDbContext>()
        .UseInMemoryDatabase("InMemoryTest")
        .Options));

这将使用单个实例设置您的模拟。每次在模拟上调用 CreateDbContext 方法时都会返回此实例。由于您的方法(正确)在每次使用后处理数据库上下文,第一次调用将处理此共享上下文,这意味着以后每次调用 CreateDbContext 都会返回已处理的实例。

您可以通过将工厂方法传递给 Returns 来更改此设置,而不是每次都创建一个新的数据库上下文:

mockDbFactory.Setup(f => f.CreateDbContext())
    .Returns(() => new SomeDbContext(new DbContextOptionsBuilder<SomeDbContext>()
        .UseInMemoryDatabase("InMemoryTest")
        .Options));

对于像您的 IDbContextFactory<> 这样的简单事物,假设它只有一个 CreateDbContext 方法,您也可以只创建一个真正的测试实现,而不是每次都创建模拟:

public class TestDbContextFactory : IDbContextFactory<SomeDbContext>
{
    private DbContextOptions<SomeDbContext> _options;

    public TestDbContextFactory(string databaseName = "InMemoryTest")
    {
        _options = new DbContextOptionsBuilder<SomeDbContext>()
            .UseInMemoryDatabase(databaseName)
            .Options;
    }

    public SomeDbContext CreateDbContext()
    {
        return new SomeDbContext(_options);
    }
}

然后你可以直接在你的测试中使用它,这可能比在这种情况下必须处理模拟更具可读性:

var repository = new Repository<SomeEntity>(new TestDbContextFactory());

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 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元字符(。)和普通点?