law*_*kwu 6 c# unit-testing xunit entity-framework-core asp.net-core
我正在为我的 Web API 编写单元测试,除非删除包含(从方法中热切加载),否则无法让测试通过。我正在使用内存数据库来提供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;
}
Run Code Online (Sandbox Code Playgroud)
DbContext这是使用内存数据库的基础:
public DataContext GetDbContext()
{
var builder = new DbContextOptionsBuilder<DataContext>();
if (useSqlite)
{
// Use Sqlite DB.
builder.UseSqlite("DataSource=:memory:", x => { });
}
else
{
// Use In-Memory DB.
builder.UseInMemoryDatabase(Guid.NewGuid().ToString());
}
var DataContext = new DataContext(builder.Options);
if (useSqlite)
{
// SQLite needs to open connection to the DB.
// Not required for in-memory-database and MS SQL.
DataContext.Database.OpenConnection();
}
DataContext.Database.EnsureCreated();
return DataContext;
}
Run Code Online (Sandbox Code Playgroud)
这是测试:
[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);
}
}
Run Code Online (Sandbox Code Playgroud)
这是我第一次编写单元测试,我基本上是边学边做。请随时指出您可能注意到的任何其他错误。
您的代码存在一些问题:
UseInMemoryDatabase数据库进行测试,因为它不支持关系行为。LibraryAssetService在本例中)。DbContext存储本地数据(在内存中),这些数据可能不存在于数据库中,并且在某些情况下可能会显示虚假的绿色测试!Attach添加资产时不需要。这可能会导致Foreign key constraintsqlite 出现错误。为了简单起见,我删除了一些导航和参数。所以让我们假设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;
}
}
Run Code Online (Sandbox Code Playgroud)
测试类:
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);
}
}
}
Run Code Online (Sandbox Code Playgroud)
最后,一个小帮助类来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);
}
Run Code Online (Sandbox Code Playgroud)