Comment puis-je invoquer un attribut de validation pour tester?

j'utilise L'attribut RegularExpressionAttribute de DataAnnotations pour la validation et j'aimerais tester mon regex. Est-il possible d'invoquer l'attribut directement dans une unité de test?

j'aimerais pouvoir faire quelque chose de similaire à ceci:

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

puis dans un test unitaire:

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

    Assert.IsTrue(dude.IsValid);
}

ou même

Assert.IsTrue(dude.PhoneNumber.IsValid);
24
demandé sur sWW 2011-03-18 19:21:49

8 réponses

j'ai fini par utiliser la classe de validation statique de L'espace de noms DataAnnotations. Mon test ressemble maintenant à ceci:

[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);
}
22
répondu CobraGeek 2013-08-05 10:35:34

Juste à la nouvelle d'un RegularExpressionAttribute objet.

var regularExpressionAttribute = new RegularExpressionAttribute("pattern");

Assert.IsTrue(regularExpressionAttribute.IsValid(objToTest));
7
répondu Martin 2011-03-18 23:30:47

Désolé d'avoir répondu en retard.

je suis nouveau ici. Si vous voulez tester chaque ValidationAttribute en isoler, vous pouvez procéder de la manière suivante par exemple:

    [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));
    }
2
répondu Evelio 2014-11-19 04:40:34

j'ai utilisé la suggestion de @Martin avec un fichier de constantes statiques qui m'a permis d'éviter de spécifier la chaîne regex localement

[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)));
}
1
répondu Jonathan Mc Namee 2014-07-08 15:00:14

Vous pouvez utiliser cette classe pour valider n'importe quel type de ValidationAttribute dans isolate: T = type de classe contenant la propriété, A = Type ValidationAttribute

Exemple:

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
répondu Evelio Ferrer 2015-09-17 20:46:56

en me basant sur la réponse de @Evelio, je vais fournir une réponse à la question de savoir comment testez-vous les validateurs personnalisés, car cela ne semble pas être articulé sur internet et c'est l'un des meilleurs résultats qui se dégagent lors de la recherche de la façon de le faire.

la réponse de@Evelio est très proche, mais il pourrait faire avec un peu plus d'une explication.

pour tester votre validation, vous devez avoir une classe qui attache les attributs de validation à ses données de membre. Ici, je suis à l'aide d'un nouveau validateur personnalisé qui a du sens pour mon projet appelé FeeTimeUnitValidator. Ce validateur prend une portée et un autre attribut comme entrée. Si l'autre attribut est zéro, alors l'attribut auquel le validateur est attaché n'a pas d'importance. Mais si l'attribut n'est pas nulle, alors cet attribut doit être dans la gamme. Voici la MockClass je utiliser pour le test:

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

notez la validation de l'attribut:

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

ceci dit de vérifier la propriété "frais" comme la propriété de frais (c.-à - d., il doit être non-zéro) et puis la gamme est de 1-12.

j'instancie la classe dans la classe de test de l'unité et l'installe avec une méthode DE setup. Puisqu'il y a trois attributs de cette classe, qui ont le validateur, je passe le nom de l'attribut dans la classe d'installation.

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

Il y a quelques choses d'intérêt. J'utilise l'approche de @Evelio pour extraire le validateur de l'attribut. C'est doe dans les lignes 3 et 4 de la routine d'installation. Puis, depuis c'est une méthode de test, je fais quelques affirme pour s'assurer que j'ai eu ce que j'attendais. Cela a en fait pris un problème lorsque j'ai transféré Ce modèle à une autre classe de test unitaire pour un autre validateur.

alors l'autre clé est que je crée le context validation (puisque les validateurs les plus compliqués ont besoin d'un contexte pour trouver les autres attributs auxquels ils se réfèrent - dans mon cas je l'utilise pour trouver l'attribut frais). Quand j'ai fait des recherches sur la façon de tester ces validateurs personnalisés, le contexte de validation me faisait trébucher. Je n'ai trouvé aucune information sur la façon de les créer. Je crois que le "contexte" pour l'attribut de validation est la classe dans laquelle l'attribut vie. C'est pourquoi je crée le contexte de validation avec l'instance de classe comme premier paramètre. Cela permet ensuite au validateur d'accéder aux autres attributs de la classe pour que vous puissiez effectuer une validation d'attribut croisé.

Maintenant que j'ai le contexte créé et un pointeur vers une validateur, je peux sauter dans l'unité de test pour s'assurer que le validateur est en train de faire son travail correctement:

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

si mon validateur n'avait pas besoin de contexte (i.e., il pourrait valider l'attribut sans référence aux autres attributs), alors je pourrais utiliser L'interface plus simple D'IsValid(), mais si le validateur a besoin d'un contexte non-null, vous devez utiliser la méthode GetValidationResult() comme je l'ai fait ici.

j'espère que cela aide quelqu'un d'autre qui pourrait être écrit validateurs et est aussi religieux que moi en ce qui concerne les tests unitaires. :)

Voici un bon article sur la création des validateurs.

0
répondu Greg Veres 2016-02-26 19:54:44
// 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
répondu sharad shrestha 2016-04-21 16:56:13

en prolongeant la réponse de @CobraGeek et le commentaire de @Erik, vous pouvez utiliser le Validator.TryValidateProperty pour valider seulement qu'un champ au lieu de l'objet entier, comme suit:

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

Après result est le booléen indiquant le succès de la validation, et si false results contient la liste des détails des erreurs levée.

0
répondu Hugo Nava Kopp 2018-01-24 12:37:40