如何在MSTest中共享WebApplicationFactory内的DBContext以获取最新数据?
问题
我定义了CustomWebApplicationFactory类和BaseTest抽象类,当前BaseTest中的ApplicationContext实例与WebApplicationFactory内的实例并非同一个。当通过HttpClient调用接口更新数据后,BaseTest中的context无法获取到最新的记录,示例代码如下:
CustomWebApplicationFactory代码
public class CustomWebApplicationFactory<TProgram> : WebApplicationFactory<Program> { private static string conn = ""; //my connection string protected override void ConfigureWebHost(IWebHostBuilder builder) { builder.ConfigureTestServices(services => { var descriptor = services.SingleOrDefault(d => d.ServiceType == typeof(DbContextOptions<ApplicationContext>)); if (descriptor != null) services.Remove(descriptor); services.AddDbContext<ApplicationContext>(options => options .UseNpgsql(conn) .UseSnakeCaseNamingConvention() .UseAllCheckConstraints() ); }); } }
BaseTest代码
[TestClass] public abstract class BaseTest { protected static CustomWebApplicationFactory<Program> _webAppFactory = new CustomWebApplicationFactory<Program>(); protected static HttpClient _httpClient = _webAppFactory.CreateDefaultClient(); protected static ApplicationContext _context; private static string conn = ""; //my connection string [ClassInitialize(InheritanceBehavior.BeforeEachDerivedClass)] public static void BaseClassInitialize(TestContext context) { var dbContextOptionsBuilder = new DbContextOptionsBuilder<ApplicationContext>(); dbContextOptionsBuilder .UseNpgsql(conn) .UseSnakeCaseNamingConvention() .UseAllCheckConstraints(); _context = new ApplicationContext(dbContextOptionsBuilder.Options); _context.Database.EnsureDeleted(); _context.Database.EnsureCreated(); } [ClassCleanup(InheritanceBehavior.BeforeEachDerivedClass)] public static void BaseClassCleanUp() { _context.Database.EnsureDeleted(); } }
测试类示例
public class CompanyTest : BaseTest { [TestMethod] public async Task UpdateCompany_ShouldReturnOk() { //Arrange var company = new UpdateCompanyCommand() { Id = _companySeed.Id, Name = "XYZ", ..rest of code }; var content = JsonConvert.SerializeObject(company); //Act var response = await _httpClient.PutAsync( "companies", new StringContent(content, Encoding.UTF8, "application/json") ); //无法获取名称更新为`XYZ`的公司 var updatedCompany = _context.Company.FirstOrDefault(); ..rest of code } }
请问如何注入/共享WebApplicationFactory中的同一个context,以便获取数据库中的最新变更?
解决方案
核心问题是你在BaseTest里手动实例化了ApplicationContext,而WebApplicationFactory内部维护的是另一个独立实例——两者共享数据库但上下文缓存相互隔离,所以看不到对方的变更。要解决这个问题,需直接从WebApplicationFactory的服务容器中获取上下文实例,并处理EF Core的缓存问题:
步骤1:修改BaseTest获取上下文的方式
删除手动创建ApplicationContext的代码,改为从WebApplicationFactory的服务容器中获取:
[TestClass] public abstract class BaseTest { protected static CustomWebApplicationFactory<Program> _webAppFactory = new CustomWebApplicationFactory<Program>(); protected static HttpClient _httpClient = _webAppFactory.CreateDefaultClient(); protected static ApplicationContext _context; [ClassInitialize(InheritanceBehavior.BeforeEachDerivedClass)] public static void BaseClassInitialize(TestContext context) { // 从WebApplicationFactory的服务容器中获取上下文实例 _context = _webAppFactory.Services.GetRequiredService<ApplicationContext>(); _context.Database.EnsureDeleted(); _context.Database.EnsureCreated(); } [ClassCleanup(InheritanceBehavior.BeforeEachDerivedClass)] public static void BaseClassCleanUp() { _context.Database.EnsureDeleted(); } }
步骤2:处理EF Core上下文缓存问题
EF Core的DbContext会缓存查询结果,调用接口更新数据后,需手动清除缓存或使用无跟踪查询才能获取最新数据:
// 方式1:清除上下文跟踪器缓存 _context.ChangeTracker.Clear(); var updatedCompany = _context.Company.FirstOrDefault(); // 方式2:使用无跟踪查询绕过缓存 var updatedCompany = _context.Company.AsNoTracking().FirstOrDefault();
步骤3:可选——实现测试间状态隔离
如果需要避免不同测试方法间的状态污染,可以在每个测试前获取新的上下文实例:
[TestInitialize] public void TestInitialize() { // 每个测试方法都获取全新的上下文实例 _context = _webAppFactory.Services.GetRequiredService<ApplicationContext>(); }
修改后,BaseTest中的_context与WebApplicationFactory内部使用的上下文来自同一个服务容器,接口变更后通过刷新缓存即可获取最新数据。
内容的提问来源于stack exchange,提问作者tickwave
相关产品推荐
相关产品推荐

