Я дам вам вариант, чтобы вы могли достичь того, чего хотите здесь, однако я должен сказать, что я не думал тщательно о каких-либо побочных эффектах, так что имейте это в виду.
Ваши валидаторы всегда будут установлены независимо от значений свойств, поэтому вам не нужно передавать экземпляр какого-либо объекта при вызове метода ShouldHaveChildValidator
. Тот факт, что они будут казнены или нет, — это отдельная история, и, как вы знаете, это будет зависеть от ваших правил.
Итак, я клонировал репозиторий git с плавной проверкой и проверил, как код проверяет наличие дочерних валидаторов.
Для этого звонка:
_validator.ShouldHaveChildValidator(i=>i.Property, typeof(FluentPropertyValidator));
Вот что он делает:
- Он получает соответствующие валидаторы для выражения свойства, которое вы передаете в вызов метода:
i => i.Property
- Он фильтрует совпадающие валидаторы, чтобы получить только те, которые относятся к типу
IChildValidatorAdaptor
.
- Выдает ошибку, если ни один из выбранных валидаторов не может быть назначен из типа, который вы передаете вызову метода:
FluentPropertyValidator
Кажется, в коде отсутствует случай, когда валидатор обернут другим валидатором. Это случай класса DelegatingValidator
, который, кстати, является типом, который использует ваш дочерний валидатор. Таким образом, одно из возможных решений — также учитывать эти типы валидаторов.
Я создал метод расширения, который вы можете использовать по тому же шаблону, что и исходный. Из-за отсутствия у меня изобретательности при присвоении имен вещам (называть имена сложно), я назвал ShouldHaveChildValidatorCustom
. Этот метод является тем же методом в коде, который также вызывает пару других методов, которые я только что скопировал из исходников FluentValidation, чтобы добавить небольшую модификацию.
Вот полный класс расширения:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using FluentValidation.Internal;
using FluentValidation.TestHelper;
using FluentValidation.Validators;
namespace YourTestExtensionsNamespace
{
public static class CustomValidationExtensions
{
public static void ShouldHaveChildValidatorCustom<T, TProperty>(this IValidator<T> validator, Expression<Func<T, TProperty>> expression, Type childValidatorType)
{
var descriptor = validator.CreateDescriptor();
var expressionMemberName = expression.GetMember()?.Name;
if (expressionMemberName == null && !expression.IsParameterExpression())
throw new NotSupportedException("ShouldHaveChildValidator can only be used for simple property expressions. It cannot be used for model-level rules or rules that contain anything other than a property reference.");
var matchingValidators = expression.IsParameterExpression() ? GetModelLevelValidators(descriptor) : descriptor.GetValidatorsForMember(expressionMemberName).ToArray();
matchingValidators = matchingValidators.Concat(GetDependentRules(expressionMemberName, expression, descriptor)).ToArray();
var childValidatorTypes = matchingValidators
.OfType<IChildValidatorAdaptor>()
.Select(x => x.ValidatorType);
//get also the validator types for the child IDelegatingValidators
var delegatingValidatorTypes = matchingValidators
.OfType<IDelegatingValidator>()
.Where(x => x.InnerValidator is IChildValidatorAdaptor)
.Select(x => (IChildValidatorAdaptor)x.InnerValidator)
.Select(x => x.ValidatorType);
childValidatorTypes = childValidatorTypes.Concat(delegatingValidatorTypes);
var validatorTypes = childValidatorTypes as Type[] ?? childValidatorTypes.ToArray();
if (validatorTypes.All(x => !childValidatorType.GetTypeInfo().IsAssignableFrom(x.GetTypeInfo())))
{
var childValidatorNames = validatorTypes.Any() ? string.Join(", ", validatorTypes.Select(x => x.Name)) : "none";
throw new ValidationTestException(string.Format("Expected property '{0}' to have a child validator of type '{1}.'. Instead found '{2}'", expressionMemberName, childValidatorType.Name, childValidatorNames));
}
}
private static IPropertyValidator[] GetModelLevelValidators(IValidatorDescriptor descriptor)
{
var rules = descriptor.GetRulesForMember(null).OfType<PropertyRule>();
return rules.Where(x => x.Expression.IsParameterExpression()).SelectMany(x => x.Validators)
.ToArray();
}
private static IEnumerable<IPropertyValidator> GetDependentRules<T, TProperty>(string expressionMemberName, Expression<Func<T, TProperty>> expression, IValidatorDescriptor descriptor)
{
var member = expression.IsParameterExpression() ? null : expressionMemberName;
var rules = descriptor.GetRulesForMember(member).OfType<PropertyRule>().SelectMany(x => x.DependentRules)
.SelectMany(x => x.Validators);
return rules;
}
}
}
И это тест, который должен быть пройден, если вы настроите дочерние валидаторы на свои классы и не пройдёте в противном случае:
[Fact]
public void ChildValidatorsSet()
{
var _validator = new FluentRemortgageInstructionValidator();
_validator.ShouldHaveChildValidatorCustom(i => i.Property, typeof(FluentPropertyValidator));
_validator.ShouldHaveChildValidatorCustom(i => i.AdditionalInformation, typeof(FluentAdditionalInformationValidator));
}
Надеюсь это поможет!
person
Karel Tamayo
schedule
18.10.2018