C# 单元测试EF核心,使用内存中的数据库和一个急切加载的函数
我正在为我的Web API编写单元测试,除非删除include(从方法中急切加载),否则无法通过测试。我正在使用内存中的数据库来提供C# 单元测试EF核心,使用内存中的数据库和一个急切加载的函数,c#,unit-testing,asp.net-core,entity-framework-core,xunit,C#,Unit Testing,Asp.net Core,Entity Framework Core,Xunit,我正在为我的Web API编写单元测试,除非删除include(从方法中急切加载),否则无法通过测试。我正在使用内存中的数据库来提供dbcontext,但不明白它为什么不返回任何数据。提前感谢您的帮助或建设性的批评 这是我正在尝试测试的方法。 注意:如果我注释掉。include语句,它将通过测试 public async Task<LibraryAsset> GetAsset(int assetId) { var asset = await _cont
dbcontext
,但不明白它为什么不返回任何数据。提前感谢您的帮助或建设性的批评
这是我正在尝试测试的方法。注意:如果我注释掉
。include
语句,它将通过测试
public async Task<LibraryAsset> GetAsset(int assetId)
{
var asset = await _context.LibraryAssets
.Include(p => p.Photo)
.Include(p => p.Category)
.Include(a => a.AssetType)
.Include(s => s.Status)
.Include(s => s.Author)
.FirstOrDefaultAsync(x => x.Id == assetId);
return asset;
}
这就是测试:
[Fact]
public async void GetAssetById_ExistingAsset_ReturnAsset()
{
using (var context = GetDbContext())
{
ILogger<LibraryAssetService> logger = new
NullLogger<LibraryAssetService>();
var service = new LibraryAssetService(context, _logger);
var asset = new LibraryAsset
{
Id = 40,
NumberOfCopies = 20,
Title = "",
Year = 1992,
Status = new Status { Id = 1 },
AssetType = new AssetType { Id = 1 },
Author = new Author { Id = 1 },
Category = new Category { Id = 2 },
Photo = new AssetPhoto { Id = 1 }
};
context.LibraryAssets.Attach(asset);
context.Add(asset);
context.SaveChanges();
var actual = await service.GetAsset(40);
Assert.Equal(40, actual.Id);
}
}
[事实]
public async void GetAssetById\u ExistingAsset\u ReturnAsset()
{
使用(var context=GetDbContext())
{
ILogger记录器=新
NullLogger();
var服务=新的LibraryAssetService(上下文,_记录器);
var资产=新图书馆资产
{
Id=40,
份数=20份,
Title=“”,
年份=1992年,
状态=新状态{Id=1},
资产类型=新资产类型{Id=1},
作者=新作者{Id=1},
类别=新类别{Id=2},
照片=新资产照片{Id=1}
};
context.LibraryAssets.Attach(资产);
添加(资产);
SaveChanges();
var实际值=等待服务.GetAsset(40);
断言.Equal(40,实际.Id);
}
}
这是我第一次编写单元测试,我基本上是边做边学。请随时指出您可能已经注意到的任何其他错误。您的代码存在一些问题:
UseInMemoryDatabase
数据库进行测试,因为它不支持关系行为李>
LibraryAssetService
)DbContext
存储数据库中可能不存在的本地数据(内存中),在某些情况下可能会显示假绿色测试李>
附加
。这可能会在sqlite中创建外键约束
错误李>
为了简单起见,我删除了一些导航和参数。因此,让我们假设LibraryAssetService
是这样的:
public class LibraryAssetService
{
public LibraryAssetService(DataContext context)
{
_context = context;
}
private readonly DataContext _context;
public async Task<LibraryAsset> GetAsset(int assetId)
{
var asset = await _context.LibraryAssets
.Include(p => p.Photo)
.Include(s => s.Author)
.FirstOrDefaultAsync(x => x.Id == assetId);
return asset;
}
}
最后,使用一个小助手类为测试准备DataContext
。在测试类之外提取此类内容是一种很好的做法在使用sqlite内存数据库进行测试时,需要记住的一点是,在测试期间应保持连接打开。无论您创建了多少个DbContext
实例。xUnit为每个测试方法创建测试类的实例。因此,将为每个测试创建一个TestDataContextFactory
实例,您就可以开始了
public class TestDataContextFactory
{
public TestDataContextFactory()
{
var builder = new DbContextOptionsBuilder<DataContext>();
var connection = new SqliteConnection("DataSource=:memory:");
connection.Open();
builder.UseSqlite(connection);
using (var ctx = new DataContext(builder.Options))
{
ctx.Database.EnsureCreated();
}
_options = builder.Options;
}
private readonly DbContextOptions _options;
public DataContext Create() => new DataContext(_options);
}
公共类TestDataContextFactory
{
公共TestDataContextFactory()
{
var builder=new DbContextOptionsBuilder();
var connection=newsqliteconnection(“数据源=:内存:”);
connection.Open();
builder.UseSqlite(连接);
使用(var ctx=newdatacontext(builder.Options))
{
ctx.Database.recreated();
}
_options=builder.options;
}
私有只读DbContextOptions\u选项;
public DataContext Create()=>新建DataContext(_选项);
}
您是否收到任何错误或意外结果?我用你的代码做了一个测试,但是没有重现你的问题。与我们分享一个演示,它可以重现您的问题。使用您的代码后,我收到一个外键限制错误。我尝试添加suppressForeignkeyenforcement,但这仍然无助于创建github回购,我会检查它。代码在我的机器上运行正常。确保您没有使用Attach()
方法。这是指向github repo@lawrencefejokwu的链接,请在LibraryAssetService
的第34行设置断点,并监视您的资产
对象。如您所见,外键值有许多0
值,如StatusId
、AssetTypeId
或AuthorId
。这就是外键限制发生的原因。数据库中没有ID为0的作者!我注释掉了LibraryAsset
中的所有外键,并使用Sqlite进行了测试,结果是绿色的!请确保您使用资产对象所需的任何东西初始化资产对象,然后就可以开始了。@lawrencefejokwu还有一件事:我检查了DataContext
类,您没有配置资产实体。为EF定义实体关系,这样可以使实体更小、更清晰。并且要注意OnModelCreating
方法中的播种。这可能会导致性能问题。
public class LibraryAssetServiceTests
{
public LibraryAssetServiceTests()
{
_factory = new TestDataContextFactory();
}
private TestDataContextFactory _factory;
[Fact]
public async void GetAssetById_ExistingAsset_ReturnAsset()
{
// Arrange
using (var context = _factory.Create())
{
var asset = new LibraryAsset
{
Id = 40,
Author = new Author { Id = 1 },
Photo = new Photo { Id = 1 }
};
context.Add(asset);
context.SaveChanges();
}
// Act
using (var context = _factory.Create())
{
var service = new LibraryAssetService(context);
var actual = await service.GetAsset(40);
// Assert
Assert.Equal(40, actual.Id);
Assert.Equal(1, actual.Author.Id);
Assert.Equal(1, actual.Photo.Id);
}
}
}
public class TestDataContextFactory
{
public TestDataContextFactory()
{
var builder = new DbContextOptionsBuilder<DataContext>();
var connection = new SqliteConnection("DataSource=:memory:");
connection.Open();
builder.UseSqlite(connection);
using (var ctx = new DataContext(builder.Options))
{
ctx.Database.EnsureCreated();
}
_options = builder.Options;
}
private readonly DbContextOptions _options;
public DataContext Create() => new DataContext(_options);
}