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
问题根源
- EventQueue状态未明确设置:作业只处理
Status == EventQueueStatus.New的队列事件,但PopulateDb中生成EventQueue时未指定Status,依赖FakerGenerator默认值,导致队列事件有时不满足查询条件,跳过更新逻辑。 - 代码变量名笔误:
PopulateDb中生成单个EventQueue实例,却调用AddRange(eventsQueue)(未定义的变量),导致队列事件未被添加到数据库,作业无数据可处理。 - DateTime时区差异:
DateTime.Parse生成的时间Kind为Unspecified,若现有事件的StartDate为UTC时间,Assert.Equal默认会比较Kind,导致值相同但断言失败。
解决方案
修正变量名并明确设置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);统一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);验证数据添加结果
在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
相关产品推荐
相关产品推荐

