solo numeros mvc ejemplos dataannotations data annotation c# validation unit-testing data-annotations dynamic-data

c# - mvc - dataannotations solo numeros



¿Cómo invoco un atributo de validación para la prueba? (8)

Estoy usando RegularExpressionAttribute from DataAnnotations para validación y me gustaría probar mi expresión regular. ¿Hay una manera de invocar el atributo directamente en una prueba de unidad?

Me gustaría poder hacer algo similar a esto:

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

Luego en una prueba unitaria:

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

O incluso

Assert.IsTrue(dude.PhoneNumber.IsValid);


Acaba de crear un objeto RegularExpressionAttribute.

var regularExpressionAttribute = new RegularExpressionAttribute("pattern"); Assert.IsTrue(regularExpressionAttribute.IsValid(objToTest));


Extendiendo la respuesta de @CobraGeek y el comentario de @Erik, puede usar Validator.TryValidateProperty para validar solo un campo en lugar de todo el objeto, así:

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

Después de lo cual el result es el booleano que indica el éxito de la validación, y si los results falsos contienen la lista de los detalles de los errores lanzados.


Lo siento por responder tarde.

Soy nuevo aqui. Si desea probar cada ValidationAttribute por separado, puede proceder a la siguiente manera, por ejemplo:

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


Puede utilizar esta clase para validar cualquier tipo de atributo de validación aislado: T = tipo de clase que contiene la propiedad, A = tipo ValidationAttribute

Ejemplo:

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


Sobre la base de la respuesta de @Evelio, voy a proporcionar una respuesta sobre cómo hacer la prueba unitaria de los validadores personalizados, ya que esto no parece estar articulado en ninguna parte de Internet y este es uno de los principales éxitos que surgen al buscar cómo hacerlo eso.

La respuesta de @Evelio es muy cercana, pero podría tener una explicación un poco más.

Para probar su validación, necesita tener una clase que adjunte atributos de validación a sus datos de miembro. Aquí estoy usando un nuevo validador personalizado que tiene sentido para mi proyecto llamado FeeTimeUnitValidator. Este validador toma un rango y otro atributo como entrada. Si el otro atributo es cero, entonces no importa el atributo al que está asociado el validador. Pero si el otro atributo no es cero, entonces este atributo debe estar en el rango. Aquí está el MockClass que uso para la prueba:

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

Observe la validación del atributo:

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

Esto indica que debe verificarse la propiedad "Tarifa" como la propiedad Tarifa (es decir, tiene que ser distinta de cero) y luego el rango es de 1 a 12.

Hago una instancia de la clase en la clase de prueba unitaria y la configuro con un método de configuración. Como hay tres atributos en esta clase que tienen el validador, paso el nombre del atributo a la clase de configuración.

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

Hay algunas cosas de interés. Estoy utilizando el enfoque de @ Evelio para extraer el validador del atributo. Esto se hace en las líneas 3 y 4 de la rutina de configuración. Luego, dado que este es un método de prueba de unidad, hago algunas afirmaciones para asegurarme de que obtuve lo que esperaba. Esto realmente detectó un problema cuando transferí este patrón a otra clase de prueba de unidad para otro validador.

Luego, la otra clave es que creo el ValidationContext (ya que los validadores más complicados necesitan un contexto para encontrar los otros atributos a los que se refieren; en mi caso, lo uso para encontrar el atributo de Tarifa). Cuando estaba investigando cómo hacer una prueba unitaria de estos validadores personalizados, lo que me estaba fallando era el ValidationContext. No pude encontrar ninguna información sobre cómo crearlos. Creo que el "contexto" para la validación del atributo es la clase en la que vive el atributo. Es por esto que creo el contexto de validación con la instancia de clase como primer parámetro. Esto proporciona al validador acceso a los otros atributos de la clase para que pueda realizar la validación de atributos cruzados.

Ahora que tengo el contexto creado y un puntero a un validador, puedo saltar a la propia prueba de la unidad para garantizar que el validador está haciendo su trabajo correctamente:

[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 mi validador no necesitaba un contexto (es decir, podría validar el atributo sin hacer referencia a los otros atributos), entonces podría usar la interfaz más simple de IsValid (), pero si el validador necesita un contexto no nulo, tiene para usar el método GetValidationResult () como lo he hecho aquí.

Espero que esto ayude a alguien más que pueda estar escribiendo validadores y que sea tan religioso con respecto a las pruebas de unidad como yo. :)

Aquí hay un buen artículo sobre la creación de validadores personalizados.


Terminé usando la clase Validator estática del espacio de nombres DataAnnotations. Mi prueba ahora se ve así:

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


Utilicé la sugerencia de @Martin junto con un archivo de constantes estáticas que me permitió evitar especificar la cadena de expresiones regulares localmente

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


// 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")); }