使用FluentValidator验证属性的子项

Pau*_*bra 6 c# fluentvalidation

我想使用FluentValidation来验证一些类,其中一个类仅用作另一个类的属性......但我从不直接创建子类,所以我想从父级别测试验证.这可能是不必要的/疯狂的

所以我举个例子

public class Parent
{
  public string Text {get;set;}
  public Child Child {get;set;}
}

public class Child 
{
  public string Text {get;set;}
}
Run Code Online (Sandbox Code Playgroud)

public class ParentValidator : AbstractValidator<Parent>
{
  public ParentValidator() 
  {
    RuleFor(p=>p.Text).NotEmpty();
  //RuleFor(p=>p.Child).SetValidator(new ChildValidator);
  //RuleFor(p=>p.Child.Text).NotEmpty();
  }
}

public class ChildValidator : AbstractValidator<Child>
{
  public ChildValidator() 
  {
    RuleFor(c=>c.Text).NotEmpty();
  }
}
Run Code Online (Sandbox Code Playgroud)

我测试使用

    [Test]
    public void ParentMustHaveText()
    {
        new ParentValidator()
             .ShouldHaveValidationErrorFor(p => p.Text, "");
    }
    [Test]
    public void ChildMustHaveText()
    {
        new ParentValidator().ShouldHaveValidationErrorFor(p => p.Child.Text, "");
    }
Run Code Online (Sandbox Code Playgroud)

ChildMustHaveText无论我如何设置,测试总是失败.我是否疯狂试图以这种方式进行测试?

因为以下测试总是通过

    [Test]
    public void ChildMustHaveText()
    {
        new ChildValidator().ShouldHaveValidationErrorFor(c => c.Text, "");
    }
Run Code Online (Sandbox Code Playgroud)

这些类是ASP.NET WebApi项目中的模型.

Evg*_*vin 6

第一个错误是您忘记Child在默认Parent构造函数中指定属性对象的创建 - FluentValidation尝试设置dynanically属性null.

public class Parent
{
    public Parent()
    {
        Child = new Child();
    }

    public string Text { get; set; }
    public Child Child { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

请注意,默认构造函数ShouldHaveValidationErrorFor在验证之前始终使用in 来创建对象.

我发现的下一件事是当前的实现 ShouldHaveValidationErrorFor 不允许检查嵌套级别大于1的嵌套属性的有效性(obj.Child1.Child2.Text例如,嵌套级别为3).

陷阱

越野车(FluentValidation.TestHelper.ValidatorTester类)的源代码:

public void ValidateError(T instanceToValidate) {
        accessor.Set(instanceToValidate, value);
        // 
        var count = validator.Validate(instanceToValidate, ruleSet: ruleSet).Errors.Count(x => x.PropertyName == accessor.Member.Name);

        if (count == 0) {
            throw new ValidationTestException(string.Format("Expected a validation error for property {0}", accessor.Member.Name));
        }
    }
Run Code Online (Sandbox Code Playgroud)

说明

方法比较加盟属性名称与验证错误(x.PropertyName)与属性对象System.Reflection.RuntimePropertyInfo名称(accessor.Member.Name),例如,"Text""Child.Text""Text" 这两个测试,所以测试通过的,只是因为parent.Text是零,它不是这两类彼此相等有效和属性名.

如果简化 - 现在你的测试通过,但错误的原因.

如果重命名string属性,可以看到这种奇怪的行为:

public class Child 
{
    public string Text2 {get;set;}
}
Run Code Online (Sandbox Code Playgroud)

或者如果Parent.Text在测试中使属性有效(删除规则,或者在Parent()默认构造函数中初始化非空值).

public Parent()
{
    Child = new Child();
    Text = "I like pitfalls";
}
Run Code Online (Sandbox Code Playgroud)

结论

这是TestHelper类中的一个错误,我希望这项研究可以帮助您确定应用程序的未来测试策略.

永不放弃!;-)