You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 11:35:22