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 WhereEnumerableIterator
1.MoveNext()
AbstractValidator1.Validate(ValidationContext
1 context) line 95
IValidator.Validate(IValidationContext context) line 48
DefaultValidatorExtensions.Validate[T](IValidator1 validator, T instance, Action
1 options) line 39
ValidationTestExtension.TestValidate[T](IValidator1 validator, T objectToTest, Action
1 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?
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 WhereEnumerableIterator
1.MoveNext()
AbstractValidator1.Validate(ValidationContext
1 context) line 95
IValidator.Validate(IValidationContext context) line 48
DefaultValidatorExtensions.Validate[T](IValidator1 validator, T instance, Action
1 options) line 39
ValidationTestExtension.TestValidate[T](IValidator1 validator, T objectToTest, Action
1 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?
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.