You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

服务层单元测试中使用内存上下文的问题求助

单元测试问题解决方案

场景回顾

现有虚构服务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);
}

关键修正点

  1. 上下文生命周期管理:将UserContext的using块包裹整个测试流程,确保在Act和Assert阶段上下文未被释放。
  2. 异步方法适配:测试方法改为async Task,并在调用UpdateUser时使用await,保证异步操作执行完成后再断言。
  3. 内存数据库隔离:每个测试用例使用不同的数据库名称,避免测试间数据污染。

内容的提问来源于stack exchange,提问作者Lukas

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.11 22:55:17