1

I'm working on a .NET 4.8 project and using FluentValidation v9.5.4. I can't update the version for reasons beyond my control.

Using the example classes from FluentValidation docs:

public class Customer 
{
    public string Name { get; set; }
    public Address Address { get; set; }
}
public class Address 
{
    public string Line1 { get; set; }
    public string Line2 { get; set; }
    public string Town { get; set; }
    public string Country { get; set; }
    public string Postcode { get; set; }
}
public class AddressValidator : AbstractValidator<Address> 
{
    public AddressValidator()
    {
        RuleFor(address => address.Postcode).NotNull();
        //etc
    }
}
public class CustomerValidator : AbstractValidator<Customer> 
{
    public CustomerValidator(IValidator<Address> addressValidator)
    {
        RuleFor(customer => customer.Name).NotNull();
        RuleFor(customer => customer.Address).SetValidator(addressValidator);
    }
}

With that setup, the validators work as expected.

When unit testing, ideally I want to test only the code of the class and mock all its dependencies. So I create some unit test like this:

public class AddressValidatorTests
{
    private readonly AddressValidator _validator;

    public AddressValidatorTests()
    {
        _validator = new AddressValidator();
    }

    [Fact]
    public void Validate_Test()
    {
        var address = new Address
        {
            Line1 = "Line number 1",
            Line2 = "Line number 2",
            Town = "Small Town",
            Country = "Some Countery",
            Postcode = "658542"
        };

        var result = _validator.TestValidate(address);

        result.ShouldNotHaveAnyValidationErrors();
    }
}
public class CustomerValidatorTests
{
    private readonly CustomerValidator _validator;

    public CustomerValidatorTests()
    {
        var addressValidatorMock = new Mock<IValidator<Address>>();
        addressValidatorMock.Setup(m => m.Validate(It.IsAny<Address>()))
            .Returns(new FluentValidation.Results.ValidationResult());

        _validator = new CustomerValidator(addressValidatorMock.Object);
    }

    [Fact]
    public void Validate_Test()
    {
        var customer = new Customer
        {
            Name = "John Doe",
            Address = new Address()
        };

        var result = _validator.TestValidate(customer);

        result.ShouldNotHaveAnyValidationErrors();
    }
}

But, when I try to run Validate_Test() on CustomerValidatorTests I get an error:

System.NullReferenceException : Object reference not set to an instance of an object.

Stack Trace:
ChildValidatorAdaptor2.Validate(PropertyValidatorContext context) line 58 PropertyRule.InvokePropertyValidator(IValidationContext context, IPropertyValidator validator, String propertyName) line 519 <Validate>d__72.MoveNext() line 351 WhereEnumerableIterator1.MoveNext()
AbstractValidator1.Validate(ValidationContext1 context) line 95
IValidator.Validate(IValidationContext context) line 48
DefaultValidatorExtensions.Validate[T](IValidator1 validator, T instance, Action1 options) line 39
ValidationTestExtension.TestValidate[T](IValidator1 validator, T objectToTest, Action1 options) line 165
ValidationTestExtension.TestValidate[T](IValidator`1 validator, T objectToTest, String ruleSet) line 147
CustomerValidatorTests.Validate_Test() line 38

I'm thinking there is something else I need to setup on the mock but it's not clear what.

What am I missing here?

1 Answer 1

3

Change your Mock setup to use Validate(ValidationContext<T> ...):

public CustomerValidatorTests()
{
    var addressValidatorMock = new Mock<IValidator<Address>>();
    addressValidatorMock.Setup(m => m.Validate(It.IsAny<ValidationContext<Address>>()))
        .Returns(new FluentValidation.Results.ValidationResult());

    _validator = new CustomerValidator(addressValidatorMock.Object);
}

Due to the internal working of the TestValidate and nested validators this overload will be invoked.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.