I have similar rules for some properties in multiple model objects and I want to replace them with custom property validators to avoid code duplication in unit tests.
I have my property validator:
public class IntIdPropertyValidator: PropertyValidator
{
public IntIdPropertyValidator()
: base("Property {PropertyName} should be greater than 0")
{
}
protected override bool IsValid(PropertyValidatorContext context)
{
var value = (int)context.PropertyValue;
return value > 0;
}
}
And wiring it up in model validator class:
public class SomeRequestValidator : AbstractValidator<CreateWordRequest>
{
public SomeRequestValidator()
{
RuleFor(x => x.Id).SetValidator(new IntIdPropertyValidator());
}
}
Tried to test:
[Test]
public void Validate_IdHasValidator_Success()
{
Init();
validator.ShouldHaveChildValidator(x => x.Id, typeof(IntIdPropertyValidator));
}
But test always fails.
So, how can I test that validator is actually set for property Id?
Generally, no. A unit test should be used to test for the functionality of a unit. You should unit test methods on a class, not individual, automatic properties (unless you are overriding the getter or setter with custom behaviour).
Characteristics of a good unit testFast: It isn't uncommon for mature projects to have thousands of unit tests. Unit tests should take little time to run. Milliseconds. Isolated: Unit tests are standalone, can be run in isolation, and have no dependencies on any outside factors such as a file system or database.
You are using ShouldHaveChildValidator
in the wrong way. Id
is a simple type.
ShouldHaveChildValidator is being in used on complex types. (see also the source code)
The right way to test the property is to pass valid objects and invalid objects and then varify using ShouldNotHaveValidationErrorFor and ShouldHaveValidationErrorFor:
[Test]
public void Should_have_error_when_Id_Is_Ilegal() {
validator.ShouldHaveValidationErrorFor(p => p.Id, new CreateWordRequest());
}
[Test]
public void Should_not_have_error_when_Id_Is_Legal() {
validator.ShouldNotHaveValidationErrorFor(p => p.Id, new CreateWordRequest()
{
Id = 7
});
}
Edit
The following code will do the verification you were looking for:
[Test]
public void Validate_IdHasValidator_Success()
{
var validator = new SomeRequestValidator();
var descriptor = validator.CreateDescriptor();
var matchingValidators = descriptor.GetValidatorsForMember(
Extensions.GetMember<CreateWordRequest, int>(x => x.Id).Name);
Assert.That(matchingValidators.FirstOrDefault(), Is.InstanceOf<IntIdPropertyValidator>());
}
I'd like to explain you the reason that you shouldn't use the above code.
When you UT class you verify that the class behavior won't be harmed.
When you create a custom validator, you create a class with a responsibility to verify specific model( --> business rules)...
Id
is a simple type with a business rules according to his parent model.
Therefore you need to verify the business rules of Id
through the model validator.
Let's assume that one of your models suddenly need to change. In this case you don't have any validation that any of you existing business rules won't harmed(or you decide to make changes inside IntIdPropertyValidator
, such a move will affect anywhere, even if you didn't want to).
Creating a custom Property Validator
is very good for code maintenance however, the tests should be against the model validator.
On complex types the story is quite different:
Usually complex types has their own business rules. In this case, you have to create a custom validator for them, and then verify that the parent validator use the right validator. Another thing to verify is: If the complex type is Null
or complex rules such as "when the property value is X and then complex type state is Y"...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With