以下测试在我运行时失败。我有一个对象instruction
,它具有许多属性,其中大多数需要自己的验证器。我希望能够检查由父验证器设置的这些子属性的验证器是否存在。
[Test]
public void ChildValidatorsSet()
{
_validator.ShouldHaveChildValidator(i => i.Property, typeof(FluentPropertyValidator));
_validator.ShouldHaveChildValidator(i => i.AdditionalInformation, typeof(FluentAdditionalInformationValidator));
}
在此类的验证器中,我定义了以下规则,以确保quest中的属性具有值集,并在该属性不为null时设置验证器。
public FluentRemortgageInstructionValidator()
{
RuleFor(si => si.Property)
.NotNull().WithMessage("Solicitor Instruction: Instructions must have a linked property.");
RuleFor(si => si.Property)
.SetValidator(new FluentPropertyValidator()).When(si => si.Property != null);
RuleFor(si => si.AdditionalInformation)
.NotNull().WithMessage("Remortgage instructions must have an additional information table.");
RuleFor(si => si.AdditionalInformation)
.SetValidator(new FluentAdditionalInformationValidator()).When(si => si.AdditionalInformation != null);
}
说明课:
public class Instruction
{
[Key]
public AdditionalInformation AdditionalInformation { get; set; }
public Property Property { get; set; }
}
}
当具有有效Property
属性的指令对象传递给验证器时,验证器应随后为Property
和AdditionalInformation
设置验证器。当我运行代码时会发生什么。
但是我无法对此进行测试,因为无法将有效对象传递给ShouldHaveChildValidator方法,因此没有设置任何子验证器。
如何设计测试以检查这些子验证器的设置正确?
答案 0 :(得分:0)
我会给您一个选择,以便您可以在这里达到想要的目标,但是我必须说,我还没有彻底考虑任何副作用,所以请记住这一点。
无论属性值如何,都将始终设置验证器,这就是为什么在调用ShouldHaveChildValidator
方法时不必传递任何对象的实例的原因。它们是否被执行的事实是另外一回事,而您所知道的将取决于您的规则集。
因此,我克隆了流畅的验证git repo,并检查了代码如何检查子验证器的存在。
为此通话:
_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));
}
希望这会有所帮助!