0

what would be the best practice for tying validation rules to specific process using http://fluentvalidation.codeplex.com/

Currently I'm using "Rule Sets" feature to group rules to different processes:

 public class ObjAValidation: AbstractValidator<A>
    {
        public ObjAValidation()
        {
            RuleSet("ProcessA", () =>
            {
                RuleFor(x => ...);
                RuleFor(x => ...);
            }); 
           RuleSet("ProcessB", () =>
            {
                RuleFor(x => ...);
                RuleFor(x => ...);
            }); 
        }
    }

And then validate using:

var a = new A(){...};
IValidator<A> validator = new ObjAValidation();
var result = validator.Validate(a, ruleSet: "ProcessA");

I have two problems with this approach:

  1. I don't like to use strings as process names. I would like to use a more strongly typed approach. For example to be able to use marker interfaces or attributes.
  2. In my Unit tests I can't setup the Validate method of IValidator because you can't use optional arguments with Moq.


Mock<IValidator<A>> _mockValidator = new Mock<IValidator<A>>();
_mockValidator.Setup(x => x.Validate(new A(), ruleSet: "ProcessA"));
Second line generates a run time error: An expression tree may not contain a named argument specification. And if you want to pass the ruleSet argument without a named argument to Validate method you have to provide a "IValidatorSelector selector" object. But this interface is not documented.
Jernej Gorički
  • 894
  • 1
  • 7
  • 16

2 Answers2

1

What prevents you to create a helper class where you can use variables, data structures, anything you like to prevent the usage of hard-coded string parameters? Also, don't forget the possibility of using enums.

What prevents you from creating a class which implements IValidator where you can also implement custom functionality needed by you?

Lajos Arpad
  • 64,414
  • 37
  • 100
  • 175
0

I have encountered the same issue and found out the reason why you can not mock this method via Moq etc validator.Validate(a, ruleSet: "ProcessA"); is because it is an extension method and Moq can't mock static methods.

Someone raised the same issue here in the FluentValidation issues: https://github.com/JeremySkinner/FluentValidation/issues/191

The simple solution is not to use the extension method rather use the instant method IValidator.validate(context). All you need to do is to build the context. Check out the source code from here: https://github.com/JeremySkinner/FluentValidation/blob/master/src/FluentValidation/DefaultValidatorExtensions.cs#L819

if(ruleSet != null) {
                var ruleSetNames = ruleSet.Split(',', ';').Select(x => x.Trim());
                selector = ValidatorOptions.ValidatorSelectors.RulesetValidatorSelectorFactory(ruleSetNames.ToArray());
            } 

            var context = new ValidationContext<T>(instance, new PropertyChain(), selector);
            return validator.Validate(context);
schrodinger's code
  • 2,624
  • 1
  • 25
  • 19