0

This is an up-to-date version of this question, as the solution no longer applies.

I've implemented fluent validation and am now building tests. The developers of FluentValidations strongly suggest that we don't mock/fake the validator, and instead use their provided InlineValidator.

Unfortunately they don't explain how the result can be configured/specified.

Someone asked this question back in 2016 and the answer was to use .RuleFor(...). However, this method no longer exists on InlineValidator.

So what's the correct way of configuring the result of a call to .Validate or .ValidateAsync when testing?

Here's what I've tried so far: I implemented my own class, derived from InlineValidator, and implemented ValidateAsync...

public class MyInlineValidator<T>(IEnumerable<ValidationFailure> validationFailures) : InlineValidator<T>
{
    public Task<ValidationResult> ValidateAsync<T>(T instance,
                                                   Action<ValidationStrategy<T>> options,
                                                   CancellationToken cancellation = default)
    {
        return Task.FromResult(new ValidationResult(validationFailures));
    }
}

But I can't get this to work when passing an instance of this into my class-under-test: the original ValidateAsync is an extension method, and the runtime keeps selecting that implementation over my custom implementation.

1
  • Added answer in the mentioned question for the version 10.0 migration. Commented May 24, 2024 at 14:48

1 Answer 1

2

From the 10.0 Upgrade Guide, you should add the ruleset as the second argument (Action<ValidationStrategy<T>> options).

List<ValidationFailure> failures = new ();
var validator = new MyInlineValidator<Customer>(failure);
ValidationResult result = await validator.ValidateAsync(cust, v => 
{
    v.IncludeRuleSets("<Rule set name>");
});

Your custom implement ValidateAsync method was incomplete, but in case you need to validate the instance and combine it with the passed-in validationFailures into the ValidationResult instance, maybe you can look the implementation as below:

public class MyInlineValidator<T>(IEnumerable<ValidationFailure> validationFailures) : InlineValidator<T>
{
    public async Task<ValidationResult> ValidateAsync<T>(T instance,
                                                   Action<ValidationStrategy<T>> options,
                                                   CancellationToken cancellation = default)
    {       
        IValidationContext  context = ValidationContext<T>.CreateWithOptions(instance, options);
        var defaultResult = new ValidationResult(validationFailures);
        
        var result = await (this as IValidator<T>).ValidateAsync(context, cancellation);
        if (result != null && !result.IsValid)
        {
            defaultResult.Errors.AddRange(result.Errors);
        }
        
        return defaultResult;
    }
}
Sign up to request clarification or add additional context in comments.

4 Comments

This still doesn't work for me - each time ValidateAsync is called, the cursor steps into the static extension method within the FluentValidations library.
Hmm, quite weird, my demo link above able to demonstrate.
I suspect that when you define the type as InlineValidator: InlineValidator<Customer> validator = new MyInlineValidator<Customer>(failures);, then this will use the default ValidateAsync method.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.