4

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

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

4

3 回答 3

6

所以这实际上很简单。答案是您需要为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;
}
于 2013-03-18T22:11:47.033 回答
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());
    }
于 2014-04-24T03:45:46.973 回答
0

只是为那些使用异步验证有相同问题的人补充一下。我需要覆盖以下内容(使用 NSubstitute)

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

注意:在我的情况下,我需要覆盖 NON-generic ValidationContext

于 2016-06-07T10:14:38.390 回答