Jezz Santos - 98 - May 20, 2014

FluentValidation.SetCollectionValidation() and injection

Given this code:

    internal class DtoARequestValidator : AbstractValidator<DtoA>
    {
        public DtoARequestValidator()
        {
            RuleFor(dto => dto.FieldA).NotEmpty();
            RuleFor(dto => dto.Items).SetCollectionValidator(new DtoBValidator());
        }
    }

    internal class DtoBValidator : AbstractValidator<DtoB>
    {
        public DtoBValidator()
        {
            RuleFor(dto => dto.FieldB).NotEmpty();
        }
    }

    public class DtoAResponse
    {
    }

    public class DtoA : IReturn<DtoAResponse>
    {
        public string FieldA { get; set; }
        public List<DtoB> Items { get; set; }
    }

    public class DtoB
    {
        public string FieldB { get; set; }
    }

DtoA has a collection of DtoB.
To unit test the DtoARequestValidator I would need to inject an instance of the DtoBValidator into the call to SetCollectionValidator()
Since SetCollectionValidator() accepts only a IValidator<T> I need to create a property on the DtoARequestValidator class that is both an interface (for mocking) and derives from IValidator<T> so I can inject it into the SetCollectionValidator() method. For example:

    internal interface IDtoBValidator : IValidator<DtoB>
    {
    }

    internal class DtoBValidator : AbstractValidator<DtoB>, IDtoBValidator
    {…}

    internal class DtoARequestValidator : AbstractValidator<DtoA>
    {
        public IDtoBValidator DtoBValidator { get; set; }

        public DtoARequestValidator()
        {
            RuleFor(dto => dto.FieldA).NotEmpty();
            RuleFor(dto => dto.Items).SetCollectionValidator(this.DtoBValidator);
        }
    }

This seems to make sense, but unfortunately at runtime, the call to container.RegisterValidators() throws an exception because it finds my interface IDtoBValidator (as it derives from IValidator<T>), but its an interface not a class, and the RegisterValidators() method tries to instantiate it, and dump it into the Funq catalog! 

What is a better pattern to use, so that I can isolate my DtoARequestValidator in unit testing???

Mike Mertsock:

Personally I don’t unit test Fluent Validation objects. It has a rather declarative design, so basically all the logic to be tested is in the fluent validation implementation itself rather than your own logic, and the effort of testing them seems far larger than the benefit. In fact, I make a rule not to add external dependencies or complicated business logic to my validator objects: I require that the only context they need is the DTO being validated, nothing external like a database repository or service class. This ensures that the validator classes stay simple and declarative. I implement more complicated validation logic in my Service classes.

+Jezz Santos I’ve updated RegisterValidators to ignore registering interface validators and included an example of just registering the validator separately in the test below.

https://github.com/ServiceStack/ServiceStack/blob/master/tests/ServiceStack.Common.Tests/ValidationTests.cs

Because configuration of the validator happens in the constructor (and you can’t register a collection validator with a delayed-loading lambda), you’ll need to use constructor injection as seen in the above test.

Did you need it to do anything else before I publish this update?

Jezz Santos:

Thanks Demis, that should do it.
+1 on ctor inject

+Jezz Santos FYI, this change is now on MyGet: https://github.com/ServiceStack/ServiceStack/wiki/MyGet