为何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
相关产品推荐
相关产品推荐

