I would like to introduce a new small library that I published – FluentValidation.Validators.UnitTestExtension. Main purpose of this library is to extend and simplify possibilities of testing code that is using FluentValidation package.
You can install it by nuget:
Install-Package FluentValidation.Validators.UnitTestExtension
or download it from GithHub.
One of approach of testing Fluent rules is presented in FluentValidation wiki page. You can find there information that FluentValidation comes with two extension methods ShouldHaveValidationErrorFor and ShouldNotHaveValidationErrorFor that can make it easier to write unit tests for validators.
And here an example of such unit tests:
readonly PersonValidator validator = new PersonValidator(); [Fact] public void Given_FirstNameIsNull_When_Validating_Then_Error() { validator.ShouldHaveValidationErrorFor(person => person.FirstName, null as string); } [Fact] public void Given_FirstNameIsEmpty_When_Validating_Then_Error() { validator.ShouldHaveValidationErrorFor(person => person.FirstName, string.Empty); } [Fact] public void Given_FirstNameIsToLong_When_Validating_Then_Error() { validator.ShouldHaveValidationErrorFor(person => person.FirstName, "Long_Test_More_Than_20_Characters"); } [Fact] public void Given_CorrectFirstName_When_Validating_Then_NoError() { validator.ShouldNotHaveValidationErrorFor(person => person.FirstName, "John"); } [Theory] [InlineData(null)] [InlineData("")] [InlineData("Long_Test_More_Than_20_Characters")] public void Given_NotCorrectLastName_When_Validating_Then_Error(string notAcceptedText) { validator.ShouldHaveValidationErrorFor(person => person.LastName, notAcceptedText); } [Fact] public void Given_CorrectLastName_When_Validating_Then_NoError() { validator.ShouldNotHaveValidationErrorFor(person => person.LastName, "John"); } [Theory] [InlineData(0)] [InlineData(-10)] [InlineData(260)] public void Given_NotCorrectHeight_When_Validating_Then_Error(int height) { validator.ShouldHaveValidationErrorFor(person => person.HeightInCentimeters, height); } [Fact] public void Given_CorrectHeight_When_Validating_Then_NoError() { validator.ShouldNotHaveValidationErrorFor(person => person.HeightInCentimeters, 150); }
With this approach you need to check validation rules for correct and not correct values of object. As you can see you will execute inner FluentValidation code and those tests are rather integration tests one rather than unit tests.
With FluentValidation package you will get also following method:
validator.ShouldHaveChildValidator(x => x.Address, typeof(AddressValidator));
But this is not enough for me. I wanted to be able to test validation configuration in very easy way. Because of that I created mentioned library. The same tests with FluentValidation.Validators.UnitTestExtension will look like this:
public class PersonValidatorTests { // Act readonly PersonValidator personValidator = new PersonValidator(); [Fact] public void Given_When_PersonValidatorConstructing_Then_3PropertiesShouldHaveRules() { // Assert personValidator.ShouldHaveRulesCount(3); } [Fact] public void Given_When_PersonValidatorConstructing_Then_RulesForFirstNameAreConfiguredCorrectly() { // Assert personValidator.ShouldHaveRules(x => x.FirstName, BaseVerifiersSetComposer.Build() .AddPropertyValidatorVerifier<NotNullValidator>() .AddPropertyValidatorVerifier<NotEmptyValidator>() .AddPropertyValidatorVerifier<LengthValidator>(0, 20) .Create()); } [Fact] public void Given_When_PersonValidatorConstructing_Then_RulesForLastNameAreConfiguredCorrectly() { // Assert personValidator.ShouldHaveRules(x => x.LastName, BaseVerifiersSetComposer.Build() .AddPropertyValidatorVerifier<NotNullValidator>() .AddPropertyValidatorVerifier<NotEmptyValidator>() .AddPropertyValidatorVerifier<LengthValidator>(0, 20) .Create()); } [Fact] public void Given_When_PersonValidatorConstructing_Then_RulesForHeightAreConfiguredCorrectly() { // Assert personValidator.ShouldHaveRules(x => x.HeightInCentimeters, BaseVerifiersSetComposer.Build() .AddPropertyValidatorVerifier<GreaterThanValidator>(0) .AddPropertyValidatorVerifier<LessThanOrEqualValidator>(250) .Create()); } }
You can see it is cleaner, run faster and easier to developed.
Hi Guys,
How do pass a List on to the ShouldNotHaveValidationErrorFor function?
eg
public void GivenValidAccountNoThenReturnsNoValidations([ValueSource(“validAccountNo”)]string accountNo)
{
validator.ShouldNotHaveValidationErrorFor(x => x[0].AccountNo.ToString(), accountNo);
}
Is this valid?
Hi,
Right now this approach is not supported.