如何在XUnit集成测试中注入IDbContextFactory?
我正在开发Blazor Server项目,创建了一个使用IDbContextFactory而非普通DbContext的泛型数据服务,该服务通过EntityFrameworkCore与数据库交互。需要为该服务编写使用真实测试数据库的XUnit集成测试,因此不打算Mock工厂。
服务基础结构如下:
public class CatalogService<T> : IEntityService<T> where T : CatalogBase { private readonly IDbContextFactory<ApplicationDbContext> _contextFactory; private readonly ILogger<T> _logger; // 工厂通过构造函数注入 public CatalogService(IDbContextFactory<ApplicationDbContext> contextFactory, ILogger<T> logger) { _contextFactory = contextFactory; _logger = logger; } //... 所有功能方法 }
我编写了用于生成测试数据的Fixture:
public class TestDatabaseFixture { private const string ConnectionString = @"my_connection"; private static readonly object _lock = new(); private static bool _databaseInitialized; public TestDatabaseFixture() { lock (_lock) { if (!_databaseInitialized) { using (var context = CreateContext()) { context.Database.EnsureDeleted(); context.Database.EnsureCreated(); // 创建品牌测试数据 for (int i = 0; i < 100; i++) { context.PcMarcas.Add(new PcBrand { Description = $"Brand {(i + 1).ToString("0000") }" }); } context.SaveChanges(); } _databaseInitialized = true; } } } public ApplicationDbContext CreateContext() { return new ApplicationDbContext( new DbContextOptionsBuilder<ApplicationDbContext>() .UseSqlServer(ConnectionString) .Options); } public IDbContextFactory<ApplicationDbContext> CreateDbContextFactory() { // 如何返回上下文工厂? <--- } }
测试代码如下:
[Fact] public async Task GetAsync_ShouldReturnAnItem() { // 初始化 ------------------------------------------------------ var contextFactory = Fixture.CreateDbContextFactory(); // 来自Fixture var logger = Mock.Of<ILogger<PcMarca>>(); var service = new CatalogService<PcMarca>(contextFactory, logger); // 执行 ------------------------------------------------------- // 断言 ---------------------------------------------------- }
请问该如何实现CreateDbContextFactory方法,从而在XUnit集成测试中注入IDbContextFactory?
你可以通过两种方式实现CreateDbContextFactory方法,核心都是返回一个符合IDbContextFactory<ApplicationDbContext>接口的实例,使其能创建连接到测试数据库的ApplicationDbContext。
方法1:使用EF Core内置的DbContextFactory
EF Core提供了内置的DbContextFactory<TContext>类,直接传入测试数据库的DbContextOptions即可完成实现:
public IDbContextFactory<ApplicationDbContext> CreateDbContextFactory() { var options = new DbContextOptionsBuilder<ApplicationDbContext>() .UseSqlServer(ConnectionString) .Options; return new DbContextFactory<ApplicationDbContext>(options); }
注意:确保你的ApplicationDbContext类有一个接收DbContextOptions<ApplicationDbContext>参数的构造函数(通常EF Core脚手架生成的上下文默认包含该构造函数)。
方法2:自定义IDbContextFactory实现
如果需要更灵活的控制,可以自己实现IDbContextFactory接口:
public IDbContextFactory<ApplicationDbContext> CreateDbContextFactory() { var options = new DbContextOptionsBuilder<ApplicationDbContext>() .UseSqlServer(ConnectionString) .Options; return new TestDbContextFactory(options); } // 内部自定义工厂类 private class TestDbContextFactory : IDbContextFactory<ApplicationDbContext> { private readonly DbContextOptions<ApplicationDbContext> _options; public TestDbContextFactory(DbContextOptions<ApplicationDbContext> options) { _options = options; } public ApplicationDbContext CreateDbContext() { return new ApplicationDbContext(_options); } }
测试代码说明
实现CreateDbContextFactory后,你的测试代码就能正常获取工厂实例并注入到CatalogService中。Fixture里的静态锁和_databaseInitialized确保测试数据库只会初始化一次,所有测试用例共享同一套测试数据,提升测试效率。
内容的提问来源于stack exchange,提问作者Omar Yafer

