xUnit+Moq单元测试EF Core InMemoryDatabase查询返回null问题
EF Core InMemoryDatabase 测试查询返回null排查方案
高频根因(按出现概率排序)
- 写入测试数据后未执行保存操作
你当前的GetInMemoryMockDB逻辑中,调用Users.AddRange()仅将测试用户标记为待添加状态,必须追加SaveChanges()或SaveChangesAsync()调用,数据才会真正写入InMemory存储。这是InMemory测试最常见的新手错误,和真实数据库的EF Core变更追踪行为完全一致,未提交的变更无法被后续查询读取。 - 上下文实例不匹配
第一,你写死了InMemory数据库名UserServiceMockDb,并行测试或者多次运行测试时,容易出现跨测试的上下文实例冲突;第二,使用AutoMock松散配置时,如果没有手动把你构造好的带数据的DataContext注册到容器中,AutoMock会自动生成一个空的Mock DataContext,查询自然返回null。
修复方式:- 每次创建InMemory上下文时,给数据库名追加随机Guid后缀,比如
$"UserServiceMockDb_{Guid.NewGuid()}",保证每个测试用例使用完全独立的内存库 - 拿到构造好的dbContext后,调用
mock.Provide(dbContext)将实例注入AutoMock容器,再通过容器Resolve UserService实例,不要手动new服务传参,避免依赖传错
- 每次创建InMemory上下文时,给数据库名追加随机Guid后缀,比如
- 大小写匹配规则差异
你用的c => c.Username.Contains(username)语法InMemory完全支持,不存在功能限制。唯一的行为差异是:InMemory做LINQ to Objects查询时默认大小写敏感,而生产环境如果用SQL Server这类默认大小写不敏感的数据库,会出现"存的是User3、传参user3,生产能查到、测试查不到"的情况,断点核对测试数据的Username字段值和入参是否完全一致即可。 - HttpContext Mock逻辑错误
断点确认GetUserRole()方法确实拿到了你Mock的admin角色Claim,走到了admin分支的查询逻辑,而不是因为Claim匹配失败走到了其他查询分支。
修正后的测试数据库初始化示例
public static async Task<DataContext> GetInMemoryMockDB() { var dbOptions = new DbContextOptionsBuilder<DataContext>() .UseInMemoryDatabase($"UserServiceMockDb_{Guid.NewGuid()}") .Options; // 配置IConfiguration的逻辑保持原有实现即可 var config = new ConfigurationBuilder() .AddJsonFile("mockAppSettings.json") .AddEnvironmentVariables() .Build(); var dbContext = new DataContext(dbOptions, config); dbContext.Database.EnsureCreated(); dbContext.Users.AddRange(GetFakeUsers()); // 必须保存才能持久化测试数据 await dbContext.SaveChangesAsync(); return dbContext; }
内容的提问来源于stack exchange,提问作者Scottish Smile
相关产品推荐
相关产品推荐

