2013-03-18 104 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