使用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语句中,则可能会出现这种情况。如果您正在使用依赖项注入,则应该让依赖项注入容器负责处理上下文实例。
您正在处理正在测试的方法中的上下文,这样您就不能再使用它了。解决这一问题的方法之一是创建一个新上下文,但您需要手动跟踪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
来获取使用相同内存中数据库实例的新上下文
注意:如果要在多个测试中使用根,您可能不希望它是静态的。
在模拟中,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();
}
}
}