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

如何为SaveAsync方法编写单元测试并验证用户保存状态?

问题背景

我需要为以下SaveAsync方法编写单元测试,该方法可正常添加并保存用户:

待测试的SaveAsync方法

public async Task<UserModel> SaveAsync(UserModel model)
{
    if (string.IsNullOrEmpty(model.ExternalUserId))
    {
        var extUser = await identityManagementService.CreateUser(model);

        user = new ApplicationUser()
        {
            ExternalUserId = extUser.UserId,
            IsActive = true,
            UserName = model.Email,
        };
        user.Id = Guid.NewGuid();
        var exists = false;
        
        await applicationUserRepository.AddOrUpdateAsync(user, a => exists);
    }
    await applicationUserRepository.SaveAsync();

    var IsSaved = await identityManagementService.GetUserById(user.ExternalUserId); // 验证用户是否保存成功

    return model;
}

我编写的单元测试代码

[Fact]
public async Task SaveAsync_Should_AddORUpdate_WhenExternalUserIdDoesNOtExsitsAndProfileImgIsNull() // 角色已存在
{
    var userModel = UserMockData.UserCorrectModelWithExternalUsserIdEmpty();
    var applicationRole = UserMockData.ApplicationRole();

    _identityManagementService.Setup(x => x.CreateUser(userModel)).Returns(Task.FromResult(UserMockData.User()));
    // _identityManagementService.Setup(x => x.GetUserById(userModel.ExternalUserId)).Returns(() => null);

    _applicationRoleRepository.Setup(x => x.FindAsync(userModel.RoleId)).Returns(Task.FromResult(applicationRole));
    _identityManagementService.Setup(x => x.AssignUserRoles(It.Is<String>(g => g != String.Empty), applicationRole.ExternalRoleId)).Returns(Task.FromResult(true));
    var sut = new UserManagementService(
        _applicationRoleRepository.Object,
        _applicationUserRepository.Object,
        _applicationRolePermissionRepository.Object,
        _identityManagementService.Object,
        _smtpEmailService.Object,
        _logger.Object
    );

    // 执行测试
    var result = await sut.SaveAsync(userModel);

    // 断言
    result.Should().NotBeNull();

    var x = _identityManagementService.Object.GetUserById(userModel.ExternalUserId).Result; // 此处返回null
    var y = _applicationRoleRepository.Object.ListAsync(false).Result?.Count(); // 此处也返回null

    x.Should().Be(1);
}

当前问题

实际调试SaveAsync方法时,var IsSaved = await identityManagementService.GetUserById(user.ExternalUserId)返回非null值,但在单元测试调试时该值为null。请指导我如何验证该方法是否成功保存用户。


解决方案

1. 修复GetUserById的Mock设置错误

测试中存在两个关键问题:

  • 你传入GetUserById的参数是userModel.ExternalUserId,但原方法中调用GetUserById时用的是CreateUser返回的extUser.UserId,而非原model的空值。
  • 未正确设置GetUserById的Mock返回值。

修改后的Mock代码:

var mockExtUser = UserMockData.User();
// 先捕获CreateUser返回的模拟用户对象
_identityManagementService.Setup(x => x.CreateUser(userModel)).Returns(Task.FromResult(mockExtUser));
// 匹配正确的参数,设置GetUserById的返回值
_identityManagementService.Setup(x => x.GetUserById(mockExtUser.UserId)).Returns(Task.FromResult(mockExtUser));

2. 用依赖调用验证替代直接结果查询(更规范的单元测试方式)

单元测试核心是验证被测方法是否正确调用了依赖组件的预期方法,而非直接查询Mock对象的结果。示例断言:

// 验证CreateUser被调用1次,参数正确
_identityManagementService.Verify(x => x.CreateUser(userModel), Times.Once);

// 验证AddOrUpdateAsync被调用1次,且传入的用户对象符合预期
_applicationUserRepository.Verify(x => x.AddOrUpdateAsync(
    It.Is<ApplicationUser>(u => u.ExternalUserId == mockExtUser.UserId && u.UserName == userModel.Email), 
    It.IsAny<Action<bool>>()), 
Times.Once);

// 验证SaveAsync被调用1次
_applicationUserRepository.Verify(x => x.SaveAsync(), Times.Once);

// 验证GetUserById被调用1次,参数为创建用户返回的UserId
_identityManagementService.Verify(x => x.GetUserById(mockExtUser.UserId), Times.Once);

3. 修正错误的断言逻辑

原测试中x.Should().Be(1)完全错误,因为GetUserById返回的是用户对象而非数字。如果要验证返回结果,应改为:

var savedUser = await _identityManagementService.Object.GetUserById(mockExtUser.UserId);
savedUser.Should().NotBeNull();
savedUser.UserId.Should().Be(mockExtUser.UserId);

4. 解决applicationRoleRepository.ListAsync返回null的问题

测试中未设置ListAsync的Mock返回值,所以调用时会返回null。如果需要验证角色列表,添加以下Mock设置:

_applicationRoleRepository.Setup(x => x.ListAsync(false)).Returns(Task.FromResult(new List<ApplicationRole> { applicationRole }));

内容的提问来源于stack exchange,提问作者hanushi-thana

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 00:35:35