2013-03-18 3 views
2

J'utilise la fluentvalidation pour effectuer une validation de modèle. J'ai une classe avec plusieurs classes imbriquées ou des collections de classes, chacune avec leur propre IValidator. Initialement, je faisais quelque chose comme ça pour mettre en place les validateurs imbriqués:test de modèle complexe avec validation imbriquée

RuleFor(foo => foo.Header).SetValidator(new FooHeaderValidator()); 

Cela fonctionne très bien. Comme j'ai commencé à implémenter plus de validateurs imbriqués, j'ai commencé à réaliser à quel point mes tests unitaires étaient fragiles pour la validation de haut niveau. Fondamentalement, toute modification apportée aux validateurs enfants peut provoquer un comportement inattendu et entraîner l'échec des tests. Évidemment, cela est dû à mon instanciation des validateurs enfants directement. Je prends maintenant cette dépendance dans l'injection via le constructeur. Cela me permet de simuler le FooHeaderValidator.

J'ai maintenant des tests échouant avec des exceptions null reference venant de quelque part dans la validation courante. Je ne peux que supposer que quelque part sur la ligne on demande quelque chose que ma maquette ne fournit pas. Ceci est la trace de la pile de 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) 

Quelqu'un at-il rencontré ce avant et savoir ce que je suis absent? Suis-je fou de se moquer de ces validateurs?

Répondre

5

Donc, c'est vraiment simple. La réponse est que vous devez configurer votre maquette pour le remplacement Validate qui accepte un ValidationContext<T>. en RhinoMocks cela ressemble à:

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

Avec sa jolie Moq similaires:

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

Belle réponse Nathan.

Voilà ma pleine mise en œuvre et tester l'unité pour un modèle qui a cinq propriétés:

/// <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

Juste pour ajouter à cela pour ceux qui ont la même question en utilisant la validation asynchrone. je devais passer outre les éléments suivants (en utilisant NSubstitute)

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

Note: Dans mon cas, je devais remplacer le ValidationContext NON générique