Как вызвать атрибут проверки для тестирования? - PullRequest
24 голосов
/ 18 марта 2011

Я использую RegularExpressionAttribute из DataAnnotations для проверки и хотел бы проверить свое регулярное выражение.Есть ли способ вызвать атрибут непосредственно в модульном тесте?

Я хотел бы иметь возможность сделать что-то похожее на это:

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 ]

22 голосов
/ 23 апреля 2011

В итоге я использовал статический класс Validator из пространства имен DataAnnotations.Мой тест теперь выглядит так:

[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);
}
7 голосов
/ 19 марта 2011

Просто новый объект RegularExpressionAttribute.

var regularExpressionAttribute = new RegularExpressionAttribute("pattern");

Assert.IsTrue(regularExpressionAttribute.IsValid(objToTest));
2 голосов
/ 19 ноября 2014

Извините за поздний ответ.

Я новичок здесь.Если вы хотите протестировать каждый атрибут 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));
    }
1 голос
/ 20 ноября 2014

Этот класс можно использовать для проверки любого типа 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)));
    }
}
1 голос
/ 08 июля 2014

Я использовал предложение @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)));
}
0 голосов
/ 24 января 2018

Расширяя ответ @ CobraGeek и комментарий @ Эрика, вы можете использовать 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 содержит список подробностей выданных ошибок.

0 голосов
/ 21 апреля 2016
// 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"));

        }
0 голосов
/ 26 февраля 2016

Опираясь на ответ @ Evelio, я собираюсь дать ответ о том, как вы проводите модульное тестирование пользовательских валидаторов, так как это, кажется, нигде не сформулировано в Интернете, и это один из самых популярных совпадений при поиске как это сделать.

@ Ответ Эвелио очень близок, но он мог бы дать немного больше объяснения.

Чтобы проверить вашу валидацию, вам нужен класс, который присоединяет атрибуты валидации к своим данным. Здесь я использую новый пользовательский валидатор, который имеет смысл для моего проекта под названием FeeTimeUnitValidator. Этот валидатор принимает диапазон и другой атрибут в качестве входных данных. Если другой атрибут равен нулю, то атрибут, к которому прикреплен валидатор, не имеет значения. Но если другой атрибут не равен нулю, этот атрибут должен находиться в диапазоне. Вот 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)]

Это говорит о том, что нужно проверять свойство "Fee" как свойство Fee (т. Е. Оно должно быть ненулевым), а затем диапазон составляет 1 - 12.

Я создаю экземпляр класса в классе модульного теста и настраиваю его с помощью метода установки. Поскольку в этом классе есть три атрибута, которые имеют валидатор, я передаю имя атрибута в класс установки.

    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». Я не мог найти информацию о том, как их создать. Я считаю, что «контекст» для проверки атрибута - это класс, в котором живет атрибут. Вот почему я создаю контекст проверки с экземпляром класса в качестве первого параметра. Это затем предоставляет валидатору доступ к другим атрибутам класса, чтобы вы могли выполнять перекрестную проверку атрибутов.

Теперь, когда у меня есть созданный контекст и указатель на валидатор, я могу перейти к самому модульному тесту, чтобы убедиться, что валидатор выполняет свою работу правильно:

    [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 (), но если валидатору нужен ненулевой контекст Вы должны использовать метод GetValidationResult (), как я сделал здесь.

Я надеюсь, что это поможет кому-то еще, кто может писать валидаторы и столь же религиозен в отношении модульного тестирования, как и я. :)

Вот хорошая статья о создании пользовательских валидаторов.

...