Visual T sharp
Visual T# (prononcé [tiː.ʃɑːp]) est un environnement de développement gratuit de tests unitaires intégré à Visual Studio, mais peut s'utiliser indépendamment. Il comprend :
AvantagesT# est un langage de programmation pour Microsoft .NET, compatible avec C# v2 (sauf en ce qui concerne le code non "managed"), et offre les avantages suivants par rapport aux solutions NUnit ou Visual Studio Team Test :
Le langageVoici un exemple d'un test minimal, écrit en T# : testclass
{
test
{
Calculatrice calc = new Calculatrice();
runtest double somme = calc.Ajouter(1, 2);
assert somme == 3;
}
}
Bonnes pratiques pour les tests unitairesT# est complètement orienté bonnes pratiques. Structure d'un testUn test unitaire est toujours composé de trois parties :
La partie la plus importante étant Exécution, c'est pour elle que vous écrivez le test. T# identifie clairement la partie Exécution en faisant commencer l'instruction par le mot clé VérificationsLa partie vérification s'assure que tous les effets (par exemple : retour de fonction, changements des paramètres, modification d'instance, de déclarations statiques, de fichiers, bases de données...) escompté lors de l'utilisation de la déclaration se sont bien effectués comme prévu. T# n'offre qu'un seul mot-clé pour cela : De plus, les conversions naturelles du langage de programmation sont utilisées (somme est un Quatre états pour un testLe test étant du code, il peut lui aussi échouer.
Contrairement aux concurrents, T# sait exactement l'instruction qui teste réellement (
T# a donc quatre états pour un test :
Afin de profiter de cette différence et de rendre le test très clair, utilisez des assert avant l'instruction testclass
{
test
{
Produit prod = new Produit("T#", 123);
runtest prod.Prix = 0;
assert prod.Prix == 0;
}
}
Dans cet exemple, on veut rendre gratuit T#. Le test passe. Le code Un bon test pour le changement du prix est donc : testclass
{
test
{
Produit prod = new Produit("T#", 123);
assert prod.Prix != 0;
runtest prod.Prix = 0;
assert prod.Prix == 0;
}
}
Maintenant, ce cas est exclu. Si le constructeur n'initialise pas la propriété Quoi tester ?T# incite à dire ce qui est testé, non pas par des noms de classes et méthodes les plus appropriés possibles, mais en l'indiquant clairement. Ainsi, le test précédent devrait s'écrire : testclass for Produit
{
test Prix set
{
Produit prod = new Produit("T#", 123);
assert prod.Prix != 0;
runtest prod.Prix = 0;
assert prod.Prix == 0;
}
}
Les avantages sont les suivants :
ContextesComme pour tout système de test, il y a beaucoup de redondances dans l'écriture des tests. En effet, il est nécessaire d'avoir plusieurs tests pour chaque déclaration d'une classe et, généralement, une classe possède plusieurs déclarations. Dans tous ces tests, il sera nécessaire de créer une instance de la classe à tester. Tous les systèmes de test proposent une méthode à appeler avant tout test et une à appeler après tout test. T#, lui, ne propose qu'une seule méthode. Cela procure ainsi les avantages suivants :
Contexte de chaque testLa forme la plus simple de contexte est le context de chaque test. C'est celle utilisée par défaut. Les tests sont exécutés, mais pas directement. Le contexte, introduit par une méthode déclarée par le mot clé Ainsi, dans notre exemple, nous voulons créer l'instance avec une seule ligne de code, mais il faut créer une instance pour chaque test : testclass for Produit
{
Produit prod;
testcontext
{
prod = new Produit("T#", 123);
runtest;
}
test Prix set // valeur minimale
{
assert prod.Prix != 0;
runtest prod.Prix = 0;
assert prod.Prix == 0;
}
test Prix set // valeur valide quelconque
{
assert prod.Prix != 12;
runtest prod.Prix = 12;
assert prod.Prix == 12;
}
}
Différents niveaux de contexteEn T#, le contexte se situe à trois niveaux :
Dans cet exemple, les tests seront exécutés deux fois, sans avoir à les écrire deux fois :
testclass
{
IDbConnection connexion;
testcontext
{
testclass
{
connexion = new SQLConnection(...);
runtest;
connexion = new OracleConnection(...);
runtest;
}
}
...
}
Quel cas tester ?Lors de l'écriture de tests unitaires, le problème le plus classique est : « Quel cas dois-je tester ? ». En effet, une même déclaration doit être testée dans différents cas. Un des exemples précédents traitait du prix d'un produit représenté par une propriété. Combien faut-il de tests et quels sont ces tests dans un tel cas? Dans les systèmes de tests classiques, c'est encore une fois le nom du test qui dit quel cas est testé (ou un commentaire, comme dans notre exemple précédent). Cela donne des noms souvent très longs et pas nécessairement clairs... ni mis à jour. T# introduit un nouveau mot clé pour exprimer le cas testé : testclass for Produit
{
Produit prod;
testcontext
{
prod = new Produit("T#", 123);
runtest;
}
test Prix set
when MinIncluded.IsMin
{
assert prod.Prix != 0;
runtest prod.Prix = 0;
assert prod.Prix == 0;
}
test Prix set
when MinIncluded.IsAboveMin
{
assert prod.Prix != 12;
runtest prod.Prix = 12;
assert prod.Prix == 12;
}
}
Cas manquantsEn fait, ce qui suit le mot clé Il suffit donc d'identifier qu'un prix de produit a une valeur minimale (0) pour identifier qu'il faut tester selon le critère Pour l'instant, nous n'avons que deux cas définis (les cas normaux). Dès la compilation, T# indique les cas manquants : Expressions de casEn réalité, après un Les opérateurs suivants existent :
Enfin, lorsqu'un cas n'a pas de sens, il est possible de demander à ne pas le prendre en compte en déclarant le cas CritèresIl existe déjà beaucoup de critères dans la bibliothèque de T#, mais cela ne peut couvrir tout vos besoins. Il est alors très facile de créer les vôtres. Un critère est comme un type énuméré, mais défini par le mot clé La convention veut que :
Ainsi, la déclaration de public criteria MinIncludedCriteria
{
[Error]
BelowMinCase,
IsMin,
IsAboveMin,
}
Tester les exceptionsComme nous l'avons vu avec les critères dans le paragraphe précédent, il est nécessaire de non seulement tester les cas normaux, mais aussi les cas d'erreur. Généralement, un cas d'erreur est rapporté par une exception. Il faut donc pouvoir tester les exceptions. Tester qu'une exception est lancéeT# vérifie les exceptions comme toute autre vérification :
Les avantages sont les suivants :
Ainsi, dans l'exemple précédent, il est nécessaire de tester le cas où le prix affecté au produit est négatif.
Comme cela n'a pas de sens, la propriété devrait générer une exception testclass for Produit
{
Produit prod;
testcontext
{
prod = new Produit("T#", 123);
runtest;
}
test Prix set
when MinIncluded.BelowMinCase
{
runtest prod.Prix = -12;
assert thrown ArgumentOutOfRangeException;
assert prod.Prix == 123; // Le prix n'a pas changé
}
...
}
Tester complètement une exceptionEn fait, cela va simplement vérifier que l'exception est bien générée dans l'instruction runtest. Ce qui est déjà bien. Cependant, il serait bon de pouvoir valider le message d'erreur par exemple. L'instruction testclass for Produit
{
Produit prod;
testcontext
{
prod = new Produit("T#", 123);
runtest;
}
test Prix set
when MinIncluded.BelowMinCase
{
runtest prod.Prix = -12;
assert thrown ArgumentOutOfRangeException e
{
assert e.Message == "Un prix ne peut être négatif!";
}
assert prod.Prix == 123; // Le prix n'a pas changé
}
...
}
Vérifier les changementsLe problème d'utiliser des contextes est que celui-ci peut se trouver physiquement loin du test que l'on travaille, et lorsqu'il est changé, peut avoir des conséquences sur l'ensemble des tests. Ainsi, dans l'exemple précédent, si le produit créé a maintenant un prix de 100 au lieu de 123, l'instruction L'idéal serait de faire des tests relatifs : conserver la valeur initiale de T# offre la possibilité d'écrire des vérifications relatives en une seule ligne de code. Vérifier la constance d'une expressionLa forme la plus simple de vérification relative est celle de la constance d'une expression. T# offre une nouvelle forme de l'instruction L'expression sera évalué avant l'instruction Ainsi, dans notre exemple, plutôt que de vérifier que le prix du produit est bien 123, il serait préférable de vérifier que le prix n'a pas changé : testclass for Produit
{
Produit prod;
testcontext
{
prod = new Produit("T#", 123);
runtest;
}
test Prix set
when MinIncluded.BelowMinCase
{
runtest prod.Prix = -12;
assert thrown ArgumentOutOfRangeException e
{
assert e.Message == "Un prix ne peut être négatif!";
}
assert !changed prod.Prix;
}
...
}
Vérifier la constance d'un objetLa forme la plus sophistiquée de vérification relative est celle de la constance d'un objet. En effet, qui dit que notre code d'affaires n'a pas modifié l'objet avant que de lancer l'exception? Dans l'instruction
Note : l'opérateur Ainsi, dans notre exemple, plutôt que de vérifier que le prix du produit n'a pas changé, il serait préférable de vérifier que l'objet testclass for Produit
{
Produit prod;
testcontext
{
prod = new Produit("T#", 123);
runtest;
}
test Prix set
when MinIncluded.BelowMinCase
{
runtest prod.Prix = -12;
assert thrown ArgumentOutOfRangeException e
{
assert e.Message == "Un prix ne peut être négatif!";
}
assert !changed prod.-*; // le produit n'a pas changé
}
...
}
Vérifier un changementSur le même principe, vérifiez qu'un changement a été apporté par une assertion relative. Une assertion relative de changement s'effectue avec l'instruction L'affectation se présente sous 3 formes :
La partie de droite est évaluée avant l'instruction runtest, et conservée, pour être comparée par égalité à la partie de gauche sur le Si nous reprenons notre exemple avec notre classe Le test de cette méthode serait : testclass for Inventaire
{
Inventaire inventaire;
testcontext
{
inventaire = new Inventaire();
runtest;
}
test Add( Produit p )
{
Product produit = new Product( "T#", 123 );
runtest inventaire.Add( produit );
assert changed inventaire.Count++; // un produit a été ajouté
assert inventaire[ inventaire.Count - 1 ] == produit; // le produit a été ajouté en dernier
}
...
}
Tester les événementsEn dehors des exceptions, les événements non plus ne sont pas faciles à tester correctement. Il n'existe aucune facilité fournie par les systèmes de tests existants. T# offre une nouvelle fois l'instruction Par exemple, une classe implémentant Note : Ce cas étant classique, nous T# fournit déjà le critère
Vérifier le non-déclenchement d'un événementLa forme la plus simple est la vérification du non déclenchement d'un événement. En T#, la vérification du non-déclenchement d'un événement s'effectue comme toujours en une ligne de code : Le compilateur T# génère une variable d'instance et une méthode compatible avec la signature de l'événement.
Dans le test, la variable est initialisée à faux, la méthode est enregistrée ( En supposant que notre classe testclass for Produit
{
Produit prod;
testcontext
{
prod = new Produit("T#", 123);
runtest;
}
test Prix set
when MinIncluded.IsAboveMin && NotifyPropertyChanged.HasSubscribersSetSameValue
{
runtest prod.Prix = prod.Prix;
assert !changed prod.-*;
assert !raised prod.PropertyChanged;
}
...
}
Vérifier le déclenchement d'un événementLa forme la plus simple de vérification du déclenchement d'un événement vérifie seulement que l'événement est déclenché. Comme toujours, T# le vérifie en une seule ligne de code : Le compilateur T# génère exactement les mêmes choses que pour Ainsi, dans notre exemple, nous devrions avoir : testclass for Produit
{
Produit prod;
testcontext
{
prod = new Produit("T#", 123);
runtest;
}
test Prix set
when MinIncluded.IsAboveMin && NotifyPropertyChanged.HasSubscribersSetOtherValue
{
assert prod.Prix != 12;
runtest prod.Prix = 12;
assert prod.Prix == 12;
assert raised prod.PropertyChanged;
}
...
}
Vérifier complètement un événementL'inconvénient de procéder comme dans le chapitre précédent, c'est que cela prouve simplement que l'événement s'est déclenché, pas que :
Une forme beaucoup plus sophistiquée de test des événements existe :
Ainsi, le même tests que dans le chapitre précédent, mais complet serait : testclass for Produit
{
Produit prod;
testcontext
{
prod = new Produit("T#", 123);
runtest;
}
test Prix set
when MinIncluded.IsAboveMin && NotifyPropertyChanged.HasSubscribersSetOtherValue
{
assert prod.Prix != 12;
runtest prod.Prix = 12;
assert prod.Prix == 12;
assert raised prod.PropertyChanged( object sender, PropertyChangedEventArgs e )
{
assert sender == prod;
assert e.PropertyName == "Prix";
assert prod.Price == 12;
}
}
...
}
Tester avec les 'Code Snippets'Visual Studio offre la possibilité d'utiliser des 'Code Snippets' dans les langages de Microsoft. Nous avons aussi ajouté des 'Code Snippets' pour Visual T# (25 dans la version 1.0). Ainsi, rien de plus facile pour générer vos tests :
Dans notre exemple, nous voulons tester le prix d'un produit. Nous avons déjà établi que les critères à utiliser sont :
Comme par hasard, il existe le 'Code Snippet' Il ne reste plus qu'à indiquer les informations suivantes :
Et voici le code généré : test Prix set
when MinIncluded.IsMin && NotifyPropertyChanged.HasNoSubscriber
{
assert prod.Prix != 0;
runtest prod.Prix = 0;
assert prod.Prix == 0;
}
test Prix set
when MinIncluded.IsMin && NotifyPropertyChanged.HasSubscribersSetSameValue
{
assert prod.Prix == 0;
runtest prod.Prix = 0;
assert !changed prod.Prix;
assert !raised prod.PropertyChanged;
}
test Prix set
when MinIncluded.IsMin && NotifyPropertyChanged.HasSubscribersSetOtherValue
{
assert prod.Prix != 0;
runtest prod.Prix = 0;
assert prod.Prix == 0;
assert raised prod.PropertyChanged( object sender, PropertyChangedEventArgs args )
{
assert sender == prod;
assert args.PropertyName == "Prix";
assert prod.Prix == 0;
}
}
test Prix set
when MinIncluded.IsAboveMin && NotifyPropertyChanged.HasNoSubscriber
{
assert prod.Prix != 12;
runtest prod.Prix = 12;
assert prod.Prix == 12;
}
test Prix set
when MinIncluded.IsAboveMin && NotifyPropertyChanged.HasSubscribersSetSameValue
{
assert prod.Prix == 12;
runtest prod.Prix = 12;
assert !changed prod.Prix;
assert !raised prod.PropertyChanged;
}
test Prix set
when MinIncluded.IsAboveMin && NotifyPropertyChanged.HasSubscribersSetOtherValue
{
assert prod.Prix != 12;
runtest prod.Prix = 12;
assert prod.Prix == 12;
assert raised prod.PropertyChanged( object sender, PropertyChangedEventArgs args )
{
assert sender == prod;
assert args.PropertyName == "Prix";
assert prod.Prix == 12;
}
}
test Prix set
when MinIncluded.BelowMinCase
{
runtest prod.Prix = -12;
assert thrown ArgumentOutOfRangeException;
assert !changed prod.-*;
}
Liens externesVisual T# est téléchargeable gratuitement sur : Forum Visual T# |
Portal di Ensiklopedia Dunia