使用xUnit和Moq测试仓储模式遇空引用异常,求学习与问题解决
问题解答
一、NullReferenceException异常原因及修复
异常根源
- Mock的DbContext未配置有效DbSet:你创建的
Mock<TodoContext>默认不会生成可用的DbSet<User>,调用_context.Set<User>()会返回null,执行Add(entity)时触发空引用异常。 - 测试逻辑错位:你Mock了
IRepository<User>,但实际测试的是真实UserRepository实例,最后却验证Mock对象的方法调用,两者完全无关,验证无效。
修复后的测试代码
要测试UserRepository的Create方法,需正确MockDbContext和DbSet,确保_context.Set<User>()返回有效Mock集合:
public class UserRepositoryTests { [Fact] public async Task Create_ShouldAddEntityAndSaveChanges() { // Arrange var userTestObject = new User { Id = 1, NickName = "johnDoe", Email = "johndoe@email.com", Password = "Pass@1234", UserTasks = new List<UserTask> { new UserTask { Id = 1, Description = "Study C#/.NET" } } }; // 创建Mock的DbSet<User> var mockDbSet = new Mock<DbSet<User>>(); // 配置Mock的TodoContext,让Set<User>()返回Mock集合 var mockContext = new Mock<TodoContext>(); mockContext.Setup(c => c.Set<User>()).Returns(mockDbSet.Object); var repository = new UserRepository(mockContext.Object); // Act var result = await repository.Create(userTestObject); // Assert // 验证DbSet的Add方法是否被调用 mockDbSet.Verify(d => d.Add(It.Is<User>(u => u == userTestObject)), Times.Once); // 验证SaveChangesAsync是否被调用一次 mockContext.Verify(c => c.SaveChangesAsync(It.IsAny<CancellationToken>()), Times.Once); // 验证返回对象与传入实体一致 Assert.Equal(userTestObject, result); } }
二、单元测试学习路径建议
- 先掌握Mock工具核心用法:比如Moq的基础操作——创建Mock对象、设置方法返回值、验证调用次数,理解Mock的作用是隔离目标对象的依赖,避免依赖真实数据库或外部服务。
- 聚焦仓储层测试核心:仓储层单元测试不需要验证EF Core的CRUD逻辑(官方已保证),重点是验证仓储方法是否正确调用
DbContext和DbSet的对应方法(如Add、Find、SaveChanges等)。 - 分阶段练习:
- 先测试无状态简单方法(如
GetById),学会MockDbSet的FindAsync返回指定数据。 - 再测试数据变更方法(如Create、Update),验证
Add/Update和SaveChangesAsync的调用。
- 先测试无状态简单方法(如
- 从官方文档入手:优先看ASP.NET Core官方单元测试指南、Moq官方基础示例,避免走弯路。
内容的提问来源于stack exchange,提问作者johnjoker13
相关产品推荐
相关产品推荐

