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

为何Mock的IGenericMongoDbRepository Exists方法始终返回false?

问题:DeleteRoleCommandValidator中BeAnExistingRole调用Exists始终返回false

我有如下DeleteRoleCommandValidator验证器类:

public class DeleteRoleCommandValidator : AbstractValidator<DeleteRoleCommand>
{
    private readonly IGenericMongoDbRepository _repository;

    public DeleteRoleCommandValidator(IGenericMongoDbRepository repository)
    {
        _repository = repository;

        RuleFor(x => x.Id).NotNull().NotEmpty();

        RuleFor(x => x.Id)
            .MustAsync(BeAnExistingRole)
           .WithMessage($"{nameof(Role)}: {nameof(DeleteRoleCommand.Id)} does not exist.")
           .When(x => !x.Id.IsNullOrBlank());

        RuleFor(x => x.Id)
            .MustAsync(NotHaveUsersForRole)
            .WithMessage($"{nameof(Role)}: {nameof(DeleteRoleCommand.Id)} has users.")
           .When(x => !x.Id.IsNullOrBlank());
    }

    private async Task<bool> BeAnExistingRole(string id, CancellationToken token)
    {
        var result = await _repository.Exists([DataFilter<Role>.Build(x => x.ItemId == id),]);
        return result;
    }

    private async Task<bool> NotHaveUsersForRole(string id, CancellationToken token)
    {
        var role = await _repository.Get([DataFilter<Role>.Build(x => x.ItemId == id)]);

        if (role is null)
            return true;

        return !await _repository.Exists(
            [DataFilter<UserRoleMap>.Build(x => x.RoleId == role.ItemId)]
        );
    }
}

对应的单元测试类:

public class DeleteRoleCommandValidatorTests
{
    private readonly DeleteRoleCommandValidator _validator;
    private readonly Mock<IGenericMongoDbRepository> _repository;

    public DeleteRoleCommandValidatorTests()
    {
        _repository = new Mock<IGenericMongoDbRepository>();
        _validator = new DeleteRoleCommandValidator(_repository.Object);
    }    

    [Fact]
    public async Task Validate_ShouldNotHaveError_WhenRoleExists()
    {
        var command = new DeleteRoleCommand { Id = "ExistingRoleId" };
        //var dataFilter = new DataFilter<Role>[] { DataFilter<Role>.Build(x => x.ItemId == command.Id) };

        _repository
            .Setup(x => x.Exists(It.IsAny<DataFilter<Role>[]>(), It.IsAny<string>()))
            .ReturnsAsync(true);

        var result = await _validator.ValidateAsync(command);

        Assert.DoesNotContain(result.Errors, e => e.PropertyName == nameof(DeleteRoleCommand.Id));
    }
}

IGenericMongoDbRepository的Exists实现:

public async Task<bool> Exists<T>(DataFilter<T>[] dataFilters, string tenantId = null)
{
    SetTenantIdContext(tenantId: tenantId);
    var filter = CreateFilterDefinition(dataFilters: dataFilters);
    return await provider.Exists(filter: filter);
}

现在遇到的问题是:BeAnExistingRole方法中调用的_repository.Exists([DataFilter<Role>.Build(x => x.ItemId == id),]);始终返回false,该如何解决?


解决方案

1. 修正Moq泛型方法匹配逻辑

你的单元测试中未明确指定Exists的泛型参数,Moq可能无法正确匹配Exists<Role>的调用,导致返回默认值false。修改测试中的Setup代码,明确指定泛型类型:

_repository
    .Setup(x => x.Exists<Role>(It.IsAny<DataFilter<Role>[]>(), It.IsAny<string>()))
    .ReturnsAsync(true);

2. 细化DataFilter的匹配条件(可选)

如果第一步修改后问题仍存在,可以进一步限定Setup的匹配逻辑,确保传入的DataFilter符合预期:

_repository
    .Setup(x => x.Exists<Role>(
        It.Is<DataFilter<Role>[]>(filters => 
            filters.Length == 1 && 
            filters[0].Expression.Compile()(new Role { ItemId = "ExistingRoleId" })
        ), 
        It.IsAny<string>()))
    .ReturnsAsync(true);

这样可以确保只有当传入的过滤条件能正确匹配目标Role时,才返回true,避免无关调用干扰。

3. 检查TenantId参数的影响

验证器调用Exists时未传入tenantId,默认值为null。如果SetTenantIdContext或CreateFilterDefinition逻辑依赖租户ID,可能导致过滤条件异常。可以在Setup中明确匹配null值的tenantId:

_repository
    .Setup(x => x.Exists<Role>(It.IsAny<DataFilter<Role>[]>(), (string)null))
    .ReturnsAsync(true);

4. 调试验证器调用逻辑

在BeAnExistingRole方法中添加断点或日志,确认传入的id值正确,且_repository是Mock实例而非真实实现。同时验证DataFilter构建的表达式是否符合预期。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 20:03:11