我正在使用fluentvalidation进行模型验证。我有一个类,有几个嵌套类或类集合,每个类都有自己的IValidator。最初我正在做这样的事情来设置嵌套的验证器:
RuleFor(foo => foo.Header).SetValidator(new FooHeaderValidator());
这非常有效。
当我开始实现更多嵌套验证器时,我开始意识到我的单元测试对于顶级验证是多么脆弱。基本上,对子验证器的任何更改都可能导致意外行为并导致测试失败。显然这是由于我直接实例化了子验证器。我现在正在通过构造函数注入获取该依赖项。这允许我嘲笑FooHeaderValidator
。
我现在测试失败,null reference
例外来自流畅验证的某个地方。我只能假设某个地方有人要求我的模拟不供应。这是来自fluentvalidation的堆栈跟踪:
at FluentValidation.Validators.ChildValidatorAdaptor.Validate(PropertyValidatorContext context)
at FluentValidation.Validators.DelegatingValidator.Validate(PropertyValidatorContext context)
at FluentValidation.Internal.PropertyRule.InvokePropertyValidator(ValidationContext context, IPropertyValidator validator, String propertyName)
at FluentValidation.Internal.PropertyRule.<Validate>d__8.MoveNext()
at System.Linq.Enumerable.<SelectManyIterator>d__14`2.MoveNext()
at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
at System.Linq.Enumerable.ToList(IEnumerable`1 source)
at FluentValidation.AbstractValidator`1.Validate(ValidationContext`1 context)
at FluentValidation.AbstractValidator`1.Validate(T instance)
有没有人遇到这个并且知道我错过了什么?我是否因为嘲笑这些验证员而感到疯狂?
答案 0 :(得分:6)
所以这实际上很简单。答案是您需要为接受Validate
的{{1}}覆盖设置模拟。在RhinoMocks中,这看起来像:
ValidationContext<T>
与Moq非常相似:
public static IValidator<T> GetMockedNestedValidator<T>()
{
var mockedValidator = MockRepository.GenerateMock<IValidator<T>>();
abstractValidator.Stub(x => x.Validate(Arg<ValidationContext<T>>.Is.Anything)).Return(new ValidationResult());
return mockedValidator;
}
答案 1 :(得分:0)
很好的回答内森。
这是我对具有五个属性的模型的完整实现和单元测试:
/// <summary>
/// Validator for the MyModel.
/// </summary>
public class Validator : AbstractValidator<MyModel>
{
/// <summary>
/// Validate the MyModel.
/// </summary>
public Validator(
IValidator<PropertyAModel> propertyAValidator,
IValidator<PropertyBModel> propertyBValidator,
IValidator<PropertyCModel> propertyCValidator,
IValidator<PropertyDModel> propertyDValidator,
IValidator<PropertyEModel> propertyEValidator)
{
RuleFor(o => o.PropertyA).SetValidator(propertyAValidator);
RuleFor(o => o.PropertyB).SetValidator(propertyBValidator);
RuleFor(o => o.PropertyC).SetValidator(propertyCValidator);
RuleFor(o => o.PropertyD).SetValidator(propertyDValidator);
RuleFor(o => o.PropertyE).SetValidator(propertyEValidator);
}
}
[TestFixture]
public class ValidatorTests : TestBase
{
private Mock<IValidator<PropertyAModel>> _mockPropertyAValidator;
private Mock<IValidator<PropertyBModel>> _mockPropertyBValidator;
private Mock<IValidator<PropertyCModel>> _mockPropertyCValidator;
private Mock<IValidator<PropertyDModel>> _mockPropertyDValidator;
private Mock<IValidator<PropertyEModel>> _mockPropertyEValidator;
private Validator _validator;
/// <Summary>
/// Setup the unit test.
/// </Summary>
[SetUp]
public void SetUp()
{
_mockPropertyAValidator = GetMockNestedValidator<PropertyAModel>();
_mockPropertyBValidator = GetMockNestedValidator<PropertyBModel>();
_mockPropertyCValidator = GetMockNestedValidator<PropertyCModel>();
_mockPropertyDValidator = GetMockNestedValidator<PropertyDModel>();
_mockPropertyEValidator = GetMockNestedValidator<PropertyEModel>();
_validator = new Validator(
_mockPropertyAValidator.Object,
_mockPropertyBValidator.Object,
_mockPropertyCValidator.Object,
_mockPropertyDValidator.Object,
_mockPropertyEValidator.Object);
}
[Test]
public void Verify_Is_Successful()
{
//
// Arrange.
//
var model = new MyModel
{
PropertyA = new PropertyAModel(),
PropertyB = new PropertyBModel(),
PropertyC = new PropertyCModel(),
PropertyD = new PropertyDModel(),
PropertyE = new PropertyEModel()
};
//
// Act.
//
_validator.Validate(model);
//
// Assert.
//
VerifyMockNestedValidator(_mockPropertyAValidator);
VerifyMockNestedValidator(_mockPropertyBValidator);
VerifyMockNestedValidator(_mockPropertyCValidator);
VerifyMockNestedValidator(_mockPropertyDValidator);
VerifyMockNestedValidator(_mockPropertyEValidator);
}
/// <summary>
/// Get a mock validator for a nested model type.
/// </summary>
/// <typeparam name="T">The type of the nested model.</typeparam>
/// <returns>The mock validator.</returns>
public static Mock<IValidator<T>> GetMockNestedValidator<T>()
{
var mockValidator = new Mock<IValidator<T>>();
mockValidator.Setup(x => x.Validate(It.IsAny<ValidationContext>())).Returns(new ValidationResult());
return mockValidator;
}
/// <summary>
/// Verify the mock validator for a nested model has called the Validate() method exactly once.
/// </summary>
/// <typeparam name="T">The type of the nested model.</typeparam>
/// <param name="mockValidator">The mock validator to verify.</param>
public static void VerifyMockNestedValidator<T>(Mock<IValidator<T>> mockValidator)
{
mockValidator.Verify(x => x.Validate(It.IsAny<ValidationContext>()), Times.Once());
}
答案 2 :(得分:0)
只是为那些使用异步验证的相同问题的人添加。 我需要覆盖以下(使用NSubstitute)
validator.ValidateAsync(Arg.Any<ValidationContext>(), Arg.Any<CancellationToken>()).Returns(Task.FromResult(new ValidationResult()));
注意:在我的情况下,我需要覆盖非泛型ValidationContext