Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to unit test simple property has validator set?

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?

like image 355
liri2006 Avatar asked Jun 27 '15 14:06

liri2006


People also ask

Do you unit test properties?

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).

What are some examples of unit testing characteristics?

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.


1 Answers

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"...

like image 118
Old Fox Avatar answered Sep 22 '22 05:09

Old Fox