如何调用验证属性进行测试?

时间:2011-03-18 16:21:49

标签: c# validation unit-testing data-annotations dynamic-data

我使用DataAnnotations中的RegularExpressionAttribute进行验证,并希望测试我的正则表达式。有没有办法直接在单元测试中调用属性?

我希望能够做类似的事情:

public class Person
{
    [RegularExpression(@"^[0-9]{3}-[0-9]{3}-[0-9]{4}$")]
    public string PhoneNumber { get; set; }
}

然后在单元测试中:

[TestMethod]
public void PhoneNumberIsValid
{
    var dude = new Person();
    dude.PhoneNumber = "555-867-5309";

    Assert.IsTrue(dude.IsValid);
}

甚至

Assert.IsTrue(dude.PhoneNumber.IsValid);

8 个答案:

答案 0 :(得分:22)

我最终使用了DataAnnotations命名空间中的静态Validator类。我的测试现在看起来像这样:

[TestMethod]
public void PhoneNumberIsValid()
{
    var dude = new Person();
    dude.PhoneNumber = "666-978-6410";

    var result = Validator.TryValidateObject(dude, new ValidationContext(dude, null, null), null, true);

    Assert.IsTrue(result);
}

答案 1 :(得分:7)

刚刚创建一个RegularExpressionAttribute对象。

var regularExpressionAttribute = new RegularExpressionAttribute("pattern");

Assert.IsTrue(regularExpressionAttribute.IsValid(objToTest));

答案 2 :(得分:2)

很抱歉回答迟到了。

我是新来的。如果要测试隔离中的每个ValidationAttribute,可以继续执行下一种方式,例如:

    [Test]
    public void Test_the_State_value_IsRequired()
    {
        string value = "Finished";
        var propertyInfo = typeof(TimeoffTemporalIncapacityEntry).GetProperty("State");
        var attribute = propertyInfo.GetCustomAttributes(typeof(RequiredAttribute), true).Cast<RequiredAttribute>().FirstOrDefault();
        Assert.IsTrue(attribute.IsValid(value));
    }

答案 3 :(得分:1)

我使用了@Martin的建议以及一个静态常量文件,它允许我避免在本地指定正则表达式字符串

[TestMethod]
public void Test_Regex_NationalinsuranceNumber()
{
    var regularExpressionAttribute = new RegularExpressionAttribute(Constants.Regex_NationalInsuranceNumber_Validate);

    List<string> validNINumbers = new List<string>() { "TN311258F", "QQ123456A" };
    List<string> invalidNINumbers = new List<string>() { "cake", "1234", "TS184LZ" };
    validNINumbers.ForEach(p => Assert.IsTrue(regularExpressionAttribute.IsValid(p)));
    invalidNINumbers.ForEach(p => Assert.IsFalse(regularExpressionAttribute.IsValid(p)));
}

答案 4 :(得分:1)

您可以使用此类验证isolate中的任何ValidationAttribute类型: T =包含属性的类类型, A =类型ValidationAttribute

示例:

string stateValue = "Pendiente";
ValidationAttributeValidator<ConfirmationTemporalIncapacityEntry, RequiredAttribute> validator =
    new ValidationAttributeValidator<ConfirmationTemporalIncapacityEntry, RequiredAttribute>();
Assert.IsTrue(validator.ValidateValidationAttribute("State", stateValue));


public class ValidationAttributeValidator<T,A>
{
    public ValidationAttributeValidator() { }

    public bool ValidateValidationAttribute(string property, object value)
    {
        var propertyInfo = typeof(T).GetProperty(property);
        var validationAttributes = propertyInfo.GetCustomAttributes(true);

        if (validationAttributes == null)
        {
            return false; 
        }
        List<ValidationAttribute> validationAttributeList = new List<ValidationAttribute>();
        foreach (object attribute in validationAttributes)
        {
            if (attribute.GetType() == typeof(A))
            {
                validationAttributeList.Add((ValidationAttribute)attribute);
            }
        }
        return(validationAttributeList.Exists(x => x.IsValid(value)));
    }
}

答案 5 :(得分:0)

在@ Evelio的回答基础上,我将提供一个答案,说明如何对自定义验证器进行单元测试,因为这似乎无法在互联网上的任何地方进行表达,这是最受欢迎的一个在搜索如何做的时候会出现。

@ Evelio的答案非常接近,但它可以做更多的解释。

要测试验证,您需要有一个将验证属性附加到其成员数据的类。在这里,我使用一个新的自定义验证器,对我的项目FeTimeUnitValidator有意义。此验证器将范围和另一个属性作为输入。如果other属性为零,那么验证器所附属的属性并不重要。但如果其他属性不为零,则此属性需要在该范围内。 这是我用于测试的MockClass:

    class MockClass
    {
        public decimal Fee { get; set; }

        [FeeTimeUnitValidator(otherPropertyName:"Fee", minValue:1, maxValue:12)]
        public int attributeUnderTest { get; set; }

        public int badOtherProperty { get; set; }
        [FeeTimeUnitValidator(otherPropertyName: "badOtherProperty", minValue: 1, maxValue: 12)]
        public int badAttributeUnderTest { get; set; }

        [FeeTimeUnitValidator(otherPropertyName: "NotFoundAttribute", minValue: 1, maxValue: 12)]
        public int nameNotFoundAttribute { get; set; }
    }

注意属性验证:

[FeeTimeUnitValidator(otherPropertyName:"Fee", minValue:1, maxValue:12)]

这表示要检查物业&#34;费用&#34;作为费用属性(即,它必须是非零),然后范围是1 - 12.

我在单元测试类中实例化类并使用setup方法进行设置。由于此类上有三个具有验证器的属性,因此我将该属性的名称传递到安装程序类中。

    private MockClass classUnderTest;
    private ValidationContext context;

    FeeTimeUnitValidator setup(string attributeUnderTest)
    {
        classUnderTest = new MockClass();
        classUnderTest.Fee = 0;
        var propertyInfo = typeof(MockClass).GetProperty(attributeUnderTest);
        var validatorArray = propertyInfo.GetCustomAttributes(typeof(FeeTimeUnitValidator), true);

        Assert.AreEqual(1, validatorArray.Length);
        var validator = validatorArray[0];
        Assert.IsTrue(validator.GetType().Equals(typeof(FeeTimeUnitValidator)));

        context = new ValidationContext(classUnderTest, null, null);

        return (FeeTimeUnitValidator)validator;
    }

有一些有趣的事情。我正在使用@ Evelio的方法从属性中提取验证器。这是设置例程的第3行和第4行。然后,由于这是一个单元测试方法,我做了一些断言,以确保我得到了我的预期。当我将此模式转移到另一个单元测试类以用于另一个验证器时,这实际上遇到了问题。

然后另一个关键是我创建了ValidationContext(因为更复杂的验证器需要一个上下文来查找它们引用的其他属性 - 在我的例子中,我用它来查找Fee属性)。当我在研究如何对这些自定义验证器进行单元测试时,惹恼我的是ValidationContext。我无法找到有关如何创建它们的任何信息。我相信&#34; context&#34;属性验证是属性所在的类。这就是我用类实例作为第一个参数创建验证上下文的原因。然后,它为验证器提供对类的其他属性的访问权限,以便您可以进行跨属性验证。

现在我已经创建了上下文并且指向了验证器,我可以跳转到单元测试本身,以确保验证器正常工作:

    [TestMethod]
    public void TestInRangeIsValidWhenFeeNonZero()
    {
        // Arrange
        var validator = setup("attributeUnderTest");
        classUnderTest.Fee = 10;

        // Act
        ValidationResult value12 = validator.GetValidationResult(12, context);
        ValidationResult value1 = validator.GetValidationResult(1, context);
        ValidationResult value5 = validator.GetValidationResult(5, context);

        // Assert
        Assert.AreEqual(ValidationResult.Success, value12);
        Assert.AreEqual(ValidationResult.Success, value1);
        Assert.AreEqual(ValidationResult.Success, value5);
    }

如果我的验证器不需要上下文(即,它可以在不引用其他属性的情况下验证属性),那么我可以使用更简单的IsValid()接口,但是如果验证器需要非null context,你必须像我在这里一样使用GetValidationResult()方法。

我希望这可以帮助那些可能正在编写验证器并且像我一样对单元测试具有宗教信仰的人。 :)

Here is a good article on creating custom validators.

答案 6 :(得分:0)

// You can do something like this.
[TestMethod]
public void PhoneNumberIsValid
{
    var propInfo = typeof(Person).GetProperty("PhoneNumber");
    var attr = propInfo.GetCustomAttributes(typeof(RegularExpressionAttribute), true);

    // Act Assert Positives
        Assert.IsTrue(((RegularExpressionAttribute)attr [0]).IsValid("555-55-5555"));

        // Act Assert Negative
      Assert.IsFalse(((RegularExpressionAttribute)attr[0]).IsValid("123654654654"));

        }

答案 7 :(得分:0)

根据@ CobraGeek的答案和@ Erik的评论,您可以使用Validator.TryValidateProperty仅验证一个字段而不是整个对象,如这样:

var results = new List<ValidationResult>();

Person dude = new Person();

System.ComponentModel.TypeDescriptor.AddProviderTransparent
(new AssociatedMetadataTypeTypeDescriptionProvider(dude.GetType()), dude.GetType());

dude.PhoneNumber = "555-867-5309";

var vc = new ValidationContext(dude, null, null);

vc.MemberName = "PhoneNumber";   

bool result = Validator.TryValidateProperty(dude.PhoneNumber, vc, results);

之后result是表示验证成功的布尔值,如果false results包含抛出的错误的详细信息列表。