UnitTest EF Core内存数据库:基础CRUD操作中Row ID不匹配问题
问题分析与解决方案
首先,我一眼就看到几个关键问题,导致你在批量运行测试时出现数据不一致的情况:
1. Create方法返回的不是实体ID,而是受影响行数
你的TestService.Create方法返回的是SaveChangesAsync()的结果,这个方法的返回值是受影响的数据库记录行数(这里插入1条记录,所以返回1),而不是新创建实体的TestId。这就是为什么你拿到的itemId是1,但实际实体的ID可能是另一个值的核心原因。
修正代码:
public async Task<int> Create(Test component) { component.TestId = 0; _context.Tests.Add(component); await _context.SaveChangesAsync(); // 返回实体自动生成的TestId,而不是受影响行数 return component.TestId; }
2. 测试方法使用async void导致异步操作未被正确等待
XUnit不支持async void类型的测试方法——这种方法会立即返回,测试框架无法等待内部的异步操作完成。在单个测试运行时,可能因为测试进程没有立即退出,异步操作碰巧有机会完成;但批量运行(RunAll)时,多个测试并行执行,异步操作还没完成,测试就已经结束,导致DbContext被提前释放、数据保存不完整,出现ID异常的情况。
修正测试方法:
[Fact] // 把async void改为async Task,让XUnit能正确等待异步操作完成 public async Task TestGet() { using (var context = new CoreContext(CoreInMemoryDbContextOptions.Get())) { var _testService = new TestService(context); var newTest = new Test { Name = "Test", }; var itemId = await _testService.Create(newTest); var items = await _testService.GetAll(); var item = await _testService.Get(itemId); Assert.Equal("Test", item.Name); } }
3. 临时DbContext未被正确释放(可选优化)
在CoreInMemoryDbContextOptions.Get()方法中,你创建了一个临时的CoreContext但没有释放它,虽然InMemory数据库不会有实际的连接泄漏,但最好养成良好的习惯,用using块确保上下文被正确释放:
public static DbContextOptions<CoreContext> Get() { var builder = new DbContextOptionsBuilder<CoreContext>(); builder.UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString()); var options = builder.Options; using (var coreContext = new CoreContext(options)) { coreContext.Database.EnsureDeleted(); coreContext.Database.EnsureCreated(); } return options; }
为什么单个测试正常,RunAll就出问题?
核心原因是async void的测试方法无法被XUnit正确等待。单个测试时,测试进程没有立即退出,异步操作有机会完成;但批量运行时,测试框架会快速调度多个测试,异步操作还没完成,using块就已经释放了DbContext,导致数据保存异常(比如ID生成错误、数据未完全写入),进而出现Get(1)返回null,但GetAll()能看到异常ID的情况。
内容的提问来源于stack exchange,提问作者Cyassin
相关产品推荐
相关产品推荐

