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

EF Core内存数据库测试不稳定:数据查询间持久化不一致

EF Core内存数据库单元测试间歇性失败问题

我在用EF Core内存数据库测试一个处理事件队列并更新数据库现有事件的作业,测试时而通过时而失败,数据检索不一致——测试有时找不到在测试设置阶段明确添加的Event实体。

问题细节

  • 在PopulateDb方法中通过DbContext向数据库添加数据
  • 作业执行时,用FirstOrDefaultAsync查询Events表
  • 明明数据已经在PopulateDb中成功添加,但该查询偶尔返回null

相关代码

Populate DB函数

private async Task PopulateDb(ApplicationDbContext dbContext)
{
    var eventQueue = FakerGenerator.EventQueue()
        .RuleFor(p => p.EventUid, "test-event-01")
        .RuleFor(e => e.Start, DateTime.Parse("2022-01-01T00:00:00"))
        .Generate();

    var existingEvent = FakerGenerator.Event()
        .RuleFor(e => e.Uid, "test-event-01")
        .RuleFor(e => e.StartDate, DateTime.Parse("1999-01-01T00:00:00"))
        .Generate();

    // 注:此处存在变量名笔误,生成的是单数eventQueue,添加的是未定义的eventsQueue
    dbContext.EventsQueue.AddRange(eventsQueue);
    var addedEventQueues = await dbContext.SaveChangesAsync();

    dbContext.Events.Add(existingEvent);
    var addedEvents = await dbContext.SaveChangesAsync();

    Assert.Equal(1, addedEventQueues);
    Assert.Equal(1, addedEvents);
}

测试函数

[Fact]
public async Task Execute_ShouldSendEmailsForNewEvents()
{
    var options = new DbContextOptionsBuilder<ApplicationDbContext>()
        .UseInMemoryDatabase(Guid.NewGuid().ToString())
        .Options;

    var dbContext = new ApplicationDbContext(options);

    await PopulateDb(dbContext);

    var emailServiceMock = new Mock<EmailService>(dbContext);

    var job = new QueuedEventNotifierJob(
        dbContext,
        emailServiceMock.Object
    );

    await job.Execute();

    var updatedEvent = await dbContext.Events.AsNoTracking().FirstAsync(e => e.Uid == "test-event-01");

    // 此处多数时候失败,偶尔成功
    Assert.Equal(DateTime.Parse("2022-01-01T00:00:00"), updatedEvent.StartDate);
}

作业中的Execute函数

public async Task Execute()
{
    var events = await dbContext.EventsQueue
        .Where(e => e.Status == EventQueueStatus.New)
        .ToListAsync();

    foreach (var unprocessedEvent in events)
    {
        var existingEvent = await dbContext.Events.FirstOrDefaultAsync(e => e.Uid == unprocessedEvent.EventUid);

        // 尝试捕获异常
        if (unprocessedEvent.EventUid == "test-event-01" && existingEvent == null)
            throw new Exception("Event not found");

        if (existingEvent != null) {
           existingEvent.StartDate = unprocessedEvent.Start;
           dbContext.Events.Update(existingEvent);
           await dbContext.SaveChangesAsync();
        }
    }
}

错误信息

Xunit.Sdk.EqualException
Assert.Equal() Failure: Values differ
Expected: 2022-01-01T00:00:00.0000000
Actual:   1999-01-01T00:00:00.0000000Z

环境信息

  • EF Core版本:8.0.7
  • 测试框架:xUnit 2.5.3
  • 内存数据库:EF Core UseInMemoryDatabase 9.0.0

问题根源

  1. EventQueue状态未明确设置:作业只处理Status == EventQueueStatus.New的队列事件,但PopulateDb中生成EventQueue时未指定Status,依赖FakerGenerator默认值,导致队列事件有时不满足查询条件,跳过更新逻辑。
  2. 代码变量名笔误:PopulateDb中生成单个EventQueue实例,却调用AddRange(eventsQueue)(未定义的变量),导致队列事件未被添加到数据库,作业无数据可处理。
  3. DateTime时区差异:DateTime.Parse生成的时间Kind为Unspecified,若现有事件的StartDate为UTC时间,Assert.Equal默认会比较Kind,导致值相同但断言失败。

解决方案

  1. 修正变量名并明确设置EventQueue状态
    确保队列事件被正确添加且状态符合作业查询条件:

    var eventQueue = FakerGenerator.EventQueue()
        .RuleFor(p => p.EventUid, "test-event-01")
        .RuleFor(e => e.Start, DateTime.Parse("2022-01-01T00:00:00"))
        .RuleFor(e => e.Status, EventQueueStatus.New) // 强制设置为待处理状态
        .Generate();
    
    // 单个实例用Add,集合用AddRange
    dbContext.EventsQueue.Add(eventQueue);
    
  2. 统一DateTime的时区或忽略Kind比较
    避免因时间Kind差异导致的断言失败:

    // 方式一:统一使用UTC时间
    var expectedDate = DateTime.SpecifyKind(DateTime.Parse("2022-01-01T00:00:00"), DateTimeKind.Utc);
    Assert.Equal(expectedDate, updatedEvent.StartDate);
    
    // 方式二:仅比较时间戳忽略Kind
    Assert.Equal(DateTime.Parse("2022-01-01T00:00:00").Ticks, updatedEvent.StartDate.Ticks);
    
  3. 验证数据添加结果
    在PopulateDb中添加队列事件后,可额外查询验证数据是否正确写入:

    var addedQueue = await dbContext.EventsQueue.FirstOrDefaultAsync(e => e.EventUid == "test-event-01");
    Assert.NotNull(addedQueue);
    Assert.Equal(EventQueueStatus.New, addedQueue.Status);
    

内容的提问来源于stack exchange,提问作者RonzyFonzy

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 16:25:58