模拟子验证器时 FluentValidation 抛出 NullReference 异常

Posted

技术标签:

【中文标题】模拟子验证器时 FluentValidation 抛出 NullReference 异常【英文标题】:NullReference exception throw by FluentValidation when mocking child validators 【发布时间】:2020-01-08 14:04:26 【问题描述】:

从简单的开始:

public interface IChild

  string Value  get; 


public class ChildValidator : AbstractValidator<IChild>

  public ChildValidator()
  
    RuleFor(c => c.Value)
      .NotEmpty()
      .NotEmpty()
      .WithMessage("Friendly Error Message");
  

然后进行测试:

static void Test_ChildValidator()

  var child = Substitute.For<IChild>();
  var validator = new ChildValidator();

  child.Value.Returns(null as string);
  validator.Validate(child).IsValid.Should().BeFalse();

  child.Value.Returns("");
  validator.Validate(child).IsValid.Should().BeFalse();

  child.Value.Returns("a");
  validator.Validate(child).IsValid.Should().BeTrue();

无例外

创建父对象和验证器:

public interface IParent

  IChild Child  get; 


public class ParentValidator : AbstractValidator<IParent>

  public ParentValidator(IValidator<IChild> childValidator)
  
    When(p => p.Child != null, () => 
      RuleFor(p => p.Child)
        .SetValidator(childValidator);
    );
  

然后使用真正的子验证器进行测试:

static void Test_ParentValidator_WithRealChildValidator()

  var child = Substitute.For<IChild>();
  var childValidator = new ChildValidator();

  var parent = Substitute.For<IParent>();
  var validator = new ParentValidator(childValidator);

  parent.Child.Returns(null as IChild);
  validator.Validate(parent).IsValid.Should().BeTrue();

  parent.Child.Returns(child);
  validator.Validate(parent).IsValid.Should().BeFalse();

  child.Value.Returns("a");
  validator.Validate(parent).IsValid.Should().BeTrue();

没有例外。

现在我尝试模拟子验证器(最终我只是想确保当子对象为空或不为空时,子验证器Validate 方法被调用或不被调用)。

static void Test_ParentValidator_WithMockedChildValidator()

  var child = Substitute.For<IChild>();
  var childValidator = Substitute.For<IValidator<IChild>>();

  var parent = Substitute.For<IParent>();
  var validator = new ParentValidator(childValidator);

  parent.Child.Returns(null as IChild);
  validator.Validate(parent).IsValid.Should().BeTrue();

  parent.Child.Returns(child);

  childValidator.Validate(Arg.Any<IChild>())
    .Returns(
      new ValidationResult(
        new List<ValidationFailure>  new ValidationFailure("property", "message") ));
  validator.Validate(parent).IsValid.Should().BeFalse();

  childValidator.Validate(Arg.Any<IChild>())
    .Returns(new ValidationResult());
  validator.Validate(parent).IsValid.Should().BeTrue();

抛出NullReferenceException

来源:“FluentValidation”

堆栈跟踪:

在 FluentValidation.Validators.ChildValidatorAdaptor.Validate(PropertyValidatorContext context) 中

/home/jskinner/code/FluentValidation/src/FluentValidation/Validators/ChildValidatorAdaptor.cs:56行

在 FluentValidation.Internal.PropertyRule.InvokePropertyValidator(ValidationContext context, IPropertyValidator validator, String propertyName) in

/home/jskinner/code/FluentValidation/src/FluentValidation/Internal/PropertyRule.cs:442行

在 FluentValidation.Internal.PropertyRule.d__65.MoveNext()

在 /home/jskinner/code/FluentValidation/src/FluentValidation/Internal/PropertyRule.cs:282 行

在 System.Linq.Enumerable.SelectManySingleSelectorIterator`2.MoveNext()

在 System.Linq.Enumerable.WhereEnumerableIterator`1.MoveNext()

在 FluentValidation.AbstractValidator1.Validate(ValidationContext1 上下文中)在

/home/jskinner/code/FluentValidation/src/FluentValidation/AbstractValidator.cs:115行

在 FluentValidation.AbstractValidator`1.Validate(T instance) in /home/jskinner/code/FluentValidation/src/FluentValidation/AbstractValidator.cs:line 83

在 SubValidationTest.Program.Test_ParentValidator_WithMockedChildValidator()

我还需要在模拟的验证器上模拟其他东西以使其正常工作吗?

pastebin - full source code

我无法在 DotNetFiddle 上(完全)让这段代码工作:(

【问题讨论】:

【参考方案1】:

从堆栈跟踪看来它失败了

 FluentValidation.AbstractValidator1.Validate(ValidationContext1 context)

这不是在模拟上配置的成员之一。

这应该符合预期

[TestMethod]
public void Test_ParentValidator_WithMockedChildValidator() 
    var child = Substitute.For<IChild>();
    var childValidator = Substitute.For<IValidator<IChild>>();
    var parent = Substitute.For<IParent>();
    var validator = new ParentValidator(childValidator);
    parent.Child.Returns(null as IChild);

    validator.Validate(parent).IsValid.Should().BeTrue();

    parent.Child.Returns(child);
    var failedResult = new ValidationResult(new List<ValidationFailure>  new ValidationFailure("property", "message") );
    childValidator.Validate(Arg.Any<ValidationContext>()).Returns(failedResult);

    validator.Validate(parent).IsValid.Should().BeFalse();

    var validResult = new ValidationResult();
    childValidator.Validate(Arg.Any<ValidationContext>()).Returns(validResult);

    validator.Validate(parent).IsValid.Should().BeTrue();

【讨论】:

是的,显然 child 验证器被不同的重载调用。

以上是关于模拟子验证器时 FluentValidation 抛出 NullReference 异常的主要内容,如果未能解决你的问题,请参考以下文章

使用父模型值的子模型验证。流畅的验证。 MVC4

验证规则构建神器 FluentValidation.md

FluentValidation:一个非常受欢迎的,用于构建强类型验证规则的.NET 库

FluentValidation 谓词验证器不起作用

模型验证组件 FluentValidation

模型验证组件 FluentValidation