2013-03-18 83 views
2

我正在使用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) 

有没有人遇到过这个,知道我错过了什么?我为嘲笑这些验证者而疯狂吗?

回答

5

所以这实际上很简单。答案是,你需要设置你的模拟Validate覆盖,接受ValidationContext<T>。在RhinoMocks这个样子:

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; 
} 

随着它的起订量非常相似:

public static Mock<IValidator<T>> GetMockedNestedValidator<T>() 
{ 
    var mockedValidator = new Mock<IValidator<T>>(); 
    abstractValidator.Setup(x => x.Validate(Arg<ValidationContext<T>>.Is.Anything)).Returns(new ValidationResult()); 
    return mockedValidator; 
} 
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()); 
    } 
0

只需添加到这个对于那些使用异步验证了同样的问题。 我需要重写以下(使用NSubstitute)

validator.ValidateAsync(Arg.Any<ValidationContext>(), Arg.Any<CancellationToken>()).Returns(Task.FromResult(new ValidationResult())); 

注:对我来说,我需要重写非通用ValidationContext