NFluent & Data Annotations : coder ses propres assertions
Nfluent fait beaucoup de choses…mais pas tout: il ne codera jamais à votre place, ne fera pas le café…mais on peut l’aider à faire des “Assert” custom pour rendre encore plus lisible nos tests unitaires.
Je vous propose donc de voir comment customiser NFluent pour valider des objet qui utilisent les attributs de validations se trouvant dans le namespace System.ComponentModel.DataAnnotations.
DataAnnotations, c’est quoi?
c’est un ensemble d’attribut permettant de spécifier des contraintes sur les propriétés d’une classe.
Par exemple, pour les classes suivantes vous avez le Nom et l’ID qui sont obligatoire, l’ID qui doit avoir une valeur comprise entre 5 et 10 et la propriété FooProperty qui à une longueur maximale de 15 caractères.
public class User
{
[Required]
public string Name { get; set; }
[Required]
[Range(5, 10)]
public int Id { get; set; }
}
public class Foo
{
[StringLength(15)]
public string FooProperty { get; set; }
}
Les DataAnnotations sont réellement utiles et s’intègrent parfaitement à vos applications (MVC et EF utilisent ces attributs pour valider les objets par exemple) et vous trouverez plus de détail ici car ce n’est pas le sujet principal de ce post.
Customiser NFluent pour s’assurer qu’un objet est valide
Il y a deux manière pour ajouter de nouvelles méthodes de validation à NFluent selon le cas d’utilisations:
- c’est une validation simple et qui ne s’enchaine pas avec d’autre méthodes de validations. On va donc avoir une assertion du type :
Check.That(user).DataAnnotationsAreValid();
2. c’est une validation qui va s’integrer dans une chaine de
validation, un truc du genre:
Check.That(user).IsOk().And.DataAnnotationsAreValid().And… ;
On va s’intéresser au second ca, le premier étant simple et limité, il n’y a pas de grand intérêt à l’expliciter ici.
1- Ecrire une méthode d’extension sur nos objets.
public static class NFluentExtention
{
public static ICheckLink<ICheck<Object>> DataAnnotationsAreValid
(this ICheck<Object> check)
{
var runnableCheck = ExtensibilityHelper.ExtractChecker<Object>
(check);
return runnableCheck.ExecuteCheck(
() =>
{
//vérifier que mon objet est OK
},
"Mon user est valide alors qu'il ne devrait pas l'être");
}
}
Notre Methode est générique et renvoi un objet de type ICheck<T> ou T est un Object…ce qui n’est pas terrible mais comme tout objet peut avoir des attributs de validation, on est obligé de prendre le plus petit dénominateur commun: la classe Object.
La première ligne de la méthode se charge de récupérer l’objet ciblé par la validation de votre méthode. Ensuite, dans le ExecuteCheck, vous allez avoir deux paramètres:
- le premier vérifie que votre objet est “valide” dans un cas normal.
- le second renvoi le message d’erreur dans le cas ou votre objet est valide alors que le résultat attendu est qu’il ne le soit pas. Comme par exemple quand on va avoir un Not avant notre méthode :
Check.That(user).Not.DataAnnotationsAreValid()
2- Ecrire le code liée à la validation de l’objet:
Il nous suffit pour cela de l’insérer à l’endroit adéquatadequat comme cela.
public static ICheckLink<ICheck<Object>> DataAnnotationsAreValid
(this ICheck<Object> check)
{
var runnableCheck = ExtensibilityHelper.ExtractChecker<Object>
(check);
return runnableCheck.ExecuteCheck(
() =>
{
var myUser = runnableCheck.Value;
var context = new ValidationContext
(myUser, serviceProvider: null, items: null);
var results = new List<ValidationResult>();
var isValid = Validator.TryValidateObject
(myUser, context, results, true);
var errorMessages = string.Empty;
if (!isValid)
{
foreach (var validationResult in results)
{
errorMessages +=
validationResult.ErrorMessage;
}
throw new FluentCheckException
("Mon user n'est pas valide :" + errorMessages);
}
},
"Mon user est valide alors qu'il ne devrait pas l'être");
}
Ici, rien de bien compliqué, on utilise les classes du framework prévu à cet effet pour s’assurer de la validité de notre objet. Dans le cas d’une erreur, on va générer une exception de type FluentCheckException.
Comment on l’utilise?
Aussi simplement qu’un autre type de validation!
[TestMethod]
public void TestMonUtilisateur_OK()
{
var user = new User();
user.Id = 6;
user.Name = "toto";
Check.That(user).DataAnnotationsAreValid();
}
[TestMethod]
public void TestFoo_OK()
{
var foo = new Foo();
foo.FooProperty = "ddddd";
Check.That(foo).DataAnnotationsAreValid();
}
[TestMethod]
public void TestFoo_KO()
{
var foo = new Foo();
foo.FooProperty = "ddzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzddd";
Check.That(foo).Not.DataAnnotationsAreValid();
}
Ce post vous a plu ? Ajoutez le dans vos favoris pour ne pas perdre de temps à le retrouver le jour où vous en aurez besoin :