Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Testing FluentValidation PropertyValidator

Is it possible to test a FluentValidation PropertyValidator in isolation?

I know I can test the Validator that's using the PropertyValidator for specific errors but I’d rather test true/false just on the property validator if possible.

Can this be done? If so, how?

like image 384
Jamez Avatar asked Jun 11 '14 11:06

Jamez


3 Answers

As for version 6.2 of FluentValidation it is possible to build the PropertyValidator.Validate() parameter due to making ValidatorSelectors globally configurable: https://github.com/JeremySkinner/FluentValidation/commit/95376c0519da1a06388be91a97fb5062fd4a162e

In the below example you see how I validate the 'puic' property of Track

Unit test:

    public void ExistsInCollectionValidatorTest()
    {
        var track = new Track()
        {
            puic = "p1"
        };

        var sut = new ExistsInCollectionValidator<Track>();

        // Build PropertyValidator.Validate() parameter
        var selector = ValidatorOptions.ValidatorSelectors.DefaultValidatorSelectorFactory();
        var context = new ValidationContext(track, new PropertyChain(), selector);
        var propertyValidatorContext = new PropertyValidatorContext(context, PropertyRule.Create<Track,string>(t => t.puic), "puic");

        var results = sut.Validate(propertyValidatorContext);
        // Assertion..
    }
like image 89
Cees Avatar answered Oct 17 '22 00:10

Cees


I know this has been a while, but I achieved this as follows:

Custom Validator:

public class MyValidator : PropertyValidator
{
    public MyValidator ()
        : base("Value must be null or between 0 and 3.")
    {
    }

    protected override bool IsValid(PropertyValidatorContext context)
    {
        if (context.PropertyValue == null)
        {
            return true;
        }

        var value = (decimal)context.PropertyValue;
        return value >= 0m && value <= 3m;
    }
}

Test Validator:

public class TestValidator : InlineValidator<TestObject>
{
    public TestValidator (params Action<TestValidator >[] actions)
    {
        foreach (var action in actions)
        {
            action(this);
        }
    }
}

Test Object:

public class TestObject
{
    public TestObject(decimal? val)
    {
        this.GenericDecimal = val;
    }

    public decimal? GenericDecimal { get; set; }
}

Test:

[Test]
public void TestIt()
{
    var validator = new TestValidator(v => v.RuleFor(obj => obj.GenericDecimal).SetValidator( new MyValidator() ));

    Assert.IsTrue(validator.Validate(new TestObject(null)).IsValid);    
    Assert.IsTrue(validator.Validate(new TestObject(0m)).IsValid);   
    Assert.IsTrue(validator.Validate(new TestObject(3m)).IsValid);   
    Assert.IsFalse(validator.Validate(new TestObject(-1m)).IsValid);   
    Assert.IsFalse(validator.Validate(new TestObject(3.01m)).IsValid);   
}
like image 40
TheMightyGherkin Avatar answered Oct 17 '22 00:10

TheMightyGherkin


I also wanted to test my true / false logic. It is a shame the IsValid method is protected. My work around was to create another IsValid method and have the protected IsValid call through to it.

public class MyValidator: PropertyValidator 
{
    public MyValidator(
        string errorMessage = "default Message") : base(errorMessage)
    {
    }

    protected override bool IsValid(PropertyValidatorContext context)
    {
        var stringToValidate = context.PropertyValue as String;
        return IsValid(stringToValidate);
    }

    public bool IsValid(string stringToValidate)
    {
        if (stringToValidate == null)
        {
            return false;
        }

        //testing logic here
        return true;
    }
}
like image 10
Rich Hildebrand Avatar answered Oct 17 '22 02:10

Rich Hildebrand