服务层单元测试中使用内存上下文的问题求助
单元测试问题解决方案
场景回顾
现有虚构服务UserService及IUserRepository实现UserRepository,需编写两个单元测试:
UpdateUser_WithSuccessfulAuthentication_UpdatesUser:认证成功时用户信息被更新UpdateUser_WithFailedAuthentication_DoesNothing:认证失败时无任何操作
原测试代码因使用using包裹UserContext,导致上下文在服务执行前被释放,无法正常运行。
修正后的测试实现
核心解决思路:将整个测试流程(Arrange/Act/Assert)都放在UserContext的using块内,确保上下文在测试全程保持可用;同时因UpdateUser是异步方法,测试方法需改为async Task类型。
测试用例1:认证成功时更新用户
public async Task UpdateUser_WithSuccessfulAuthentication_UpdatesUser() { // Arrange var authenticationServiceMock = new Mock<IAuthenticationService>(); var contextOptions = new DbContextOptionsBuilder<UserContext>() .UseInMemoryDatabase(databaseName: "TestDb_Success") .Options; // 所有操作都在上下文的using块内执行,避免提前释放 using var context = new UserContext(contextOptions); var userRepository = new UserRepository(context); var userService = new UserService(userRepository, authenticationServiceMock.Object); // 模拟认证成功的返回结果 authenticationServiceMock .Setup(s => s.Authenticate(It.IsAny<int>())) .ReturnsAsync(new AuthenticationDetails { Success = true }); // 准备测试数据:先插入初始用户 var initialUserDto = GetUserTestDoubleToCreate(); userRepository.Insert(initialUserDto); await context.SaveChangesAsync(); // 确保数据写入内存库 var updateUserDto = GetUserTestDoubleToUpdate(); updateUserDto.Id = initialUserDto.Id; // 确保更新的是同一个用户 // Act await userService.UpdateUser(updateUserDto); // Assert var updatedUser = userRepository.GetUser(initialUserDto.Id); Assert.Equal(updateUserDto.Name, updatedUser.Name); // 示例断言,根据实际DTO字段调整 }
测试用例2:认证失败时无操作
public async Task UpdateUser_WithFailedAuthentication_DoesNothing() { // Arrange var authenticationServiceMock = new Mock<IAuthenticationService>(); var contextOptions = new DbContextOptionsBuilder<UserContext>() .UseInMemoryDatabase(databaseName: "TestDb_Failure") .Options; using var context = new UserContext(contextOptions); var userRepository = new UserRepository(context); var userService = new UserService(userRepository, authenticationServiceMock.Object); // 模拟认证失败的返回结果 authenticationServiceMock .Setup(s => s.Authenticate(It.IsAny<int>())) .ReturnsAsync(new AuthenticationDetails { Success = false }); // 插入初始用户并保存 var initialUserDto = GetUserTestDoubleToCreate(); userRepository.Insert(initialUserDto); await context.SaveChangesAsync(); var originalUserName = initialUserDto.Name; var updateUserDto = GetUserTestDoubleToUpdate(); updateUserDto.Id = initialUserDto.Id; // Act await userService.UpdateUser(updateUserDto); // Assert:用户信息未被修改 var unchangedUser = userRepository.GetUser(initialUserDto.Id); Assert.Equal(originalUserName, unchangedUser.Name); }
关键修正点
- 上下文生命周期管理:将
UserContext的using块包裹整个测试流程,确保在Act和Assert阶段上下文未被释放。 - 异步方法适配:测试方法改为
async Task,并在调用UpdateUser时使用await,保证异步操作执行完成后再断言。 - 内存数据库隔离:每个测试用例使用不同的数据库名称,避免测试间数据污染。
内容的提问来源于stack exchange,提问作者Lukas
相关产品推荐
相关产品推荐

