【问题标题】:How to unit test a single Fluent Validation Rule wit NUnit?如何使用 NUnit 对单个 Fluent Validation Rule 进行单元测试?
【发布时间】:2021-08-26 19:28:01
【问题描述】:

我有一个流畅的验证器配置,如下所示:

        public class Validator : AbstractValidator<Command>
        {
            public Validator(IDbContext dbContext)
            {
                RuleFor(c => c.PersonId)
                    .EntityExists<Command, Person>(dbContext, nameof(Command.PersonId));

                RuleFor(c => c.ProjectId)
                    .Cascade(CascadeMode.Stop)
                    .EntityExists<Command, Project>(dbContext, nameof(Command.ProjectId))
                    .MustAsync(async (projectId, cancellation) => !(await dbContext.FindAsync<Project>(new object[] { projectId }, cancellation)).IsCompleted)
                    .WithMessage("The project is completed. You may not set the participation of a completed project");

                RuleFor(c => c.StatusId)
                    .EntityExists<Command, Status>(dbContext, nameof(Command.StatusId))

                ...
            }
        }

我想用 NUnit、NSubstitute 和 Fluent Validation Extensions 对第二条规则进行单元测试:

        [Test]
        public void Should_Not_Have_Validation_Error_If_Valid_ProjectId_Is_Supplied()
        {
            _dbContext.EntityExistsAsync<Project>(Arg.Any<Guid>(), Arg.Any<CancellationToken>()).Returns(true);
            _validator.ShouldNotHaveValidationErrorFor(command => command.ProjectId, Guid.NewGuid());
        }

由于缺少 PersonId,测试失败。我正在测试 ProjectId 的验证规则,但测试包括 PersonId 的验证规则。如果我模拟 dbContext 以使 PersonId 规则通过,则测试会因为缺少 StatusId 而失败,这也是单独定义的验证规则。

如何在不必模拟模型中所有其他属性的规则的情况下测试一个单独属性的规则?

【问题讨论】:

  • 您可以尝试写入要测试单独功能的每个规则。在 Validator 函数中只需调用所有函数。并在测试中对每一个功能进行测试。如果您编写的函数是私有的,请查看这篇文章:stackoverflow.com/questions/249847/…
  • 感谢您的评论。使用这种方法,我可以单独测试验证功能,但不能测试模型的验证配置。

标签: c# nunit .net-5 fluentvalidation


【解决方案1】:

validate 方法允许您使用选项并指定要检查的属性。 (默认情况下它包括所有属性的规则)

因此,这里是一个示例(此代码将仅验证 StatudId 的规则)

_validator.Validate(command, options => 
{
  options.IncludeProperties(x => x.StatusId);
});

更多细节可以在官方网站上找到。 https://docs.fluentvalidation.net/en/latest/start.html#throwing-exceptions

【讨论】:

    【解决方案2】:

    在@Евгений Елисеев 的提示下(谢谢!)我能够编写仅测试单个属性规则的测试扩展:

            public static IEnumerable<ValidationFailure> ShouldHaveValidationErrorForExact<T, TValue>(this IValidator<T> validator,
                Expression<Func<T, TValue>> expression, TValue value) where T : class, new()
            {
                var instanceToValidate = new T();
    
                var memberAccessor = new MemberAccessor<T, TValue>(expression, true);
                memberAccessor.Set(instanceToValidate, value);
    
                TestValidationResult<T> testValidationResult = validator.TestValidate(instanceToValidate, opt => opt.IncludeProperties(memberAccessor.Member.Name));
                return testValidationResult.ShouldHaveValidationErrorFor(expression);
            }
    
            public static IEnumerable<ValidationFailure> ShouldHaveValidationErrorForExact<T, TValue>(this IValidator<T> validator, Expression<Func<T, TValue>> expression, T objectToTest) where T : class
            {
                TValue value = expression.Compile()(objectToTest);
                var memberAccessor = new MemberAccessor<T, TValue>(expression, true);
                TestValidationResult<T> testValidationResult = validator.TestValidate(objectToTest, opt => opt.IncludeProperties(memberAccessor.Member.Name));
                return testValidationResult.ShouldHaveValidationErrorFor(expression);
            }
    
            public static void ShouldNotHaveValidationErrorForExact<T, TValue>(this IValidator<T> validator,
                Expression<Func<T, TValue>> expression, TValue value) where T : class, new()
            {
    
                var instanceToValidate = new T();
    
                var memberAccessor = new MemberAccessor<T, TValue>(expression, true);
                memberAccessor.Set(instanceToValidate, value);
    
                TestValidationResult<T> testValidationResult = validator.TestValidate(instanceToValidate, opt => opt.IncludeProperties(memberAccessor.Member.Name));
                testValidationResult.ShouldNotHaveValidationErrorFor(expression);
            }
    
            public static void ShouldNotHaveValidationErrorForExact<T, TValue>(this IValidator<T> validator, Expression<Func<T, TValue>> expression, T objectToTest) where T : class
            {
                TValue value = expression.Compile()(objectToTest);
                var memberAccessor = new MemberAccessor<T, TValue>(expression, true);
                TestValidationResult<T> testValidationResult = validator.TestValidate(objectToTest, opt => opt.IncludeProperties(memberAccessor.Member.Name));
                testValidationResult.ShouldNotHaveValidationErrorFor(expression);
            }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2010-09-15
      • 1970-01-01
      • 2011-02-20
      • 2019-12-31
      • 1970-01-01
      • 1970-01-01
      • 2018-03-04
      相关资源
      最近更新 更多