Test unitaire succinct Visual Studio

Ceci est un extrait de l'eBook Unit Testing Succinctly de Marc Clifton, gracieusement fourni par Syncfusion..

Un test unitaire est composé de deux choses:

  • Une classe représentant le montage de test.
  • Méthodes dans la classe représentant les tests unitaires.

Visual Studio crée automatiquement un stub pour un projet de test. C'est ici que nous allons commencer..

Création d'un projet de test unitaire dans Visual Studio

Les tests unitaires sont généralement placés dans un projet distinct (résultant d'un assemblage distinct) à partir du code de votre application. Dans Visual Studio 2008 ou 2012, vous pouvez créer un projet de test unitaire en cliquant avec le bouton droit de la souris sur la solution et en sélectionnant Ajouter suivi par Nouveau projet dans le menu contextuel:

Ajout d'un nouveau projet

Dans la boîte de dialogue qui apparaît, sélectionnez un Tester projet:

Nouveau projet de test VS2008 Nouveau projet de test VS2012

Visual Studio 2008 créera un fichier de raccord, «UnitTest1.cs» (si vous avez sélectionné le langage C #), avec divers commentaires utiles dans le raccord. Visual Studio 2012 crée un talon beaucoup plus incertain:

en utilisant le système; using Microsoft.VisualStudio.TestTools.UnitTesting; espace de noms VS2012UnitTestProject1 [TestClass] classe publique UnitTest1 [TestMethod] public void TestMethod1 ()  

Pour les utilisateurs de Visual Studio 2008

Visual Studio 2008 créera également une classe TestContext. Celle-ci n'existe plus dans VS2012 et nous l'ignorerons. Nous utiliserons le fichier de remplacement précédent de VS2012..

Supprimez également le fichier «ManualTest1.mht», sinon vous serez invité à sélectionner les résultats du test et à saisir les notes de test manuellement..

Appareils d'essai

Notez que la classe est décorée avec l'attribut TestClass. Ceci définit le montage de test - une collection de méthodes de test.

Méthodes d'essai

Notez que la méthode est décorée avec l'attribut TestMethod. Ceci définit une méthode, que le montage de test exécutera.


La classe Assert

La classe assert définit les méthodes statiques suivantes pouvant être utilisées pour vérifier le calcul d'une méthode:

  • AreEqual / AreNotEqual
  • AreSame / AreNotSame
  • IsTrue / IsFalse
  • IsNull / IsNotNull
  • IsInstanceOfType / IsNotInstanceOfType

Beaucoup de ces assertions sont surchargées et il est recommandé de consulter la documentation complète fournie par Microsoft..

Principes fondamentaux de l'affirmation

Notez que les exemples suivants utilisent VS2008.

Les assertions sont la colonne vertébrale de chaque test. Il existe une variété d’affirmations concernant les résultats d’un test. Pour commencer, nous écrirons une simple affirmation qui dit «on égal un», autrement dit, un truisme:

[TestClass] public class UnitTest1 [TestMethod] public void TestMethod1 () Assert.IsTrue (1 == 1);  

Exécutez le test, ce qui devrait aboutir à «réussi»:

Assertion simple

AreEqual / AreNotEqual

Les méthodes AreEqual et AreNotEqual comparent:

  • objets
  • double
  • simple
  • des cordes
  • données typées

Ils consistent à comparer la valeur attendue (le premier paramètre) à la valeur réelle (le deuxième paramètre). En ce qui concerne les valeurs simples et doubles, il est possible de spécifier «avec une certaine précision». Enfin, toutes les surcharges ont la possibilité d’afficher un message (éventuellement formaté) si l’assertion échoue.

En ce qui concerne l'égalité d'objet, cette méthode compare si les instances sont identiques:

classe publique AnObject  [TestMethod] public void ObjectEqualityTest () AnObject object1 = new AnObject (); AnObject object2 = new AnObject (); Assert.AreNotEqual (object1, object2);  

Le test précédent réussit, car object1 et object2 ne sont pas égaux. Toutefois, si la classe remplace la méthode Equals, l'égalité est basée sur la comparaison effectuée par la méthode Equals implémentée dans la classe. Par exemple:

public class AnObject public int SomeValue get; ensemble;  public override bool Equals (object obj) return SomeValue == ((AnObject) obj) .SomeValue;  [TestMethod] public void ObjectEqualityTest () AnObject object1 = new AnObject () SomeValue = 1; AnObject object2 = new AnObject () SomeValue = 1; Assert.AreEqual (object1, object2);  

AreSame / AreNotSame

Ces deux méthodes vérifient que le les instances sont les mêmes (ou pas). Par exemple:

[TestMethod] public void SamenessTest () AnObject object1 = new AnObject () SomeValue = 1; AnObject object2 = new AnObject () SomeValue = 1; Assert.AreNotSame (object1, object2);  

Même si la classe AnObject remplace l’opérateur Equals, le test précédent réussit car le les instances des deux objets ne sont pas les mêmes.

IsTrue / IsFalse

Ces deux méthodes vous permettent de tester la véracité d'une comparaison de valeur. Du point de vue de la lisibilité, les méthodes IsTrue et IsFalse sont généralement utilisées pour valeur comparaisons, alors que AreEqual et AreSame sont généralement utilisés pour comparer les instances (objets).

Par exemple:

[TestMethod] public void IsTrueTest () AnObject object1 = new AnObject () SomeValue = 1; Assert.IsTrue (object1.SomeValue == 1);  

Ceci vérifie la valeur d'une propriété.

IsNull / IsNotNull

Ces deux tests vérifient si un objet est null ou non:

[TestMethod] public void IsNullTest () AnObject object1 = null; Assert.IsNull (object1);  

IsInstanceOfType / IsNotInstanceOfType

Ces deux méthodes vérifient qu'un objet est une instance d'un type spécifique (ou non). Par exemple:

public class AnotherObject  [TestMethod] public void TypeOfTest () AnObject object1 = new AnObject (); Assert.IsNotInstanceOfType (object1, typeof (AnotherObject));  

Non concluante

La méthode Assert.Inconclusive peut être utilisée pour spécifier que le test ou la fonctionnalité derrière le test n'a pas encore été implémenté et que, par conséquent, le test n'est pas concluant..

Que se passe-t-il lorsqu'une assertion échoue??

En ce qui concerne le test des unités Visual Studio, lorsqu'une assertion échoue, la méthode Assert lève une exception AssertFailedException. Cette exception ne devrait jamais être gérée par votre code de test.


Autres classes d'assertion

Il existe deux autres classes d'assertion:

  • CollectionAssert
  • StringAssert

Comme leur nom l’indique, ces assertions s’appliquent respectivement aux collections et aux chaînes..

Assertions de collection

Ces méthodes sont implémentées dans la classe Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert. Notez que le paramètre collection de ces méthodes s'attend à ce que la collection implémente ICollection (par opposition à NUnit, qui attend IEnumerable)..

AllItemsAreInstanceOfType

Cette assertion vérifie que les objets d'une collection sont du même type, ce qui inclut les types dérivés du type attendu, comme illustré ici:

public class A  public class B: A  [TestClass] public class CollectionTests [TestMethod] public void InstancesOfTypeTest () List points = new List () new Point (1, 2), nouveau Point (3, 4 ); liste items = nouvelle liste() nouveau B (), nouveau A (); CollectionAssert.AllItemsAreInstancesOfType (points, typeof (Point)); CollectionAssert.AllItemsAreInstancesOfType (éléments, typeof (A));   

AllItemsAreNotNull

Cette assertion vérifie que les objets de la collection ne sont pas nuls.

AllItemsAreUnique

Ce test garantit que les objets d'une collection sont uniques. Si comparant des structures:

[TestMethod] public void AreUniqueTest () List points = new List () new Point (1, 2), new Point (1, 2); CollectionAssert.AllItemsAreUnique (points);  

les structures sont comparées par valeur et non par instance, le test précédent échouant. Cependant, même si la classe remplace la méthode Equals:

public class AnObject public int SomeValue get; ensemble;  public override bool Equals (object obj) return SomeValue == ((AnObject) obj) .SomeValue;  

ce test réussit:

[TestMethod] public void AreUniqueObjectsTest () List items = nouvelle liste() new AnObject () SomeValue = 1, new AnObject () SomeValue = 1; CollectionAssert.AllItemsAreUnique (éléments);   

AreEqual / AreNotEqual

Ces tests affirment que deux collections sont égales. Les méthodes incluent des surcharges qui vous permettent de fournir une méthode de comparaison. Si un objet remplace la méthode Equals, cette méthode sera utilisée pour déterminer l'égalité. Par exemple:

[TestMethod] public void AreEqualTest () List itemList1 = nouvelle liste() new AnObject () SomeValue = 1, new AnObject () SomeValue = 2; liste itemList2 = nouvelle liste() new AnObject () SomeValue = 1, new AnObject () SomeValue = 2; CollectionAssert.AreEqual (itemList1, itemList2);   

Ces deux collections sont égales car la classe AnObject substitue la méthode Equals (voir exemple précédent).

Notez que pour passer l'assertion, les listes doivent être de la même longueur et ne sont pas considérées égales si les listes sont identiques sauf dans un ordre différent. Comparez cela avec l'assertion AreEquivalent décrite ci-après..

AreEquivalent / AreNotEquivalent

Cette assertion compare deux listes et considère que les listes d'éléments égaux sont équivalentes quel que soit leur ordre. Malheureusement, il semble y avoir un bogue dans la mise en œuvre, car ce test échoue:

[TestMethod] public void AreEqualTest () List itemList1 = nouvelle liste() new AnObject () SomeValue = 1, new AnObject () SomeValue = 2; liste itemList2 = nouvelle liste() new AnObject () SomeValue = 2, new AnObject () SomeValue = 1; CollectionAssert.AreEquivalent (itemList1, itemList2);     Le bogue AreEquivalent de Visual Studio  

avec le message d'erreur:

CollectionAssert.AreEquivalent a échoué. La collection attendue contient 1 occurrence (s) de. La collection actuelle contient 0 occurrence (s). 

Attendu que la mise en œuvre de cette affirmation par NUnit passe:

Les travaux AreEquivalent de NUnit correctement

Contient / DoesNotContain

Cette assertion vérifie qu'un objet est contenu dans une collection:

[TestMethod] public void ContainsTest () List itemList = nouvelle liste() new AnObject () SomeValue = 1, new AnObject () SomeValue = 2; CollectionAssert.Contains (itemList, new AnObject () SomeValue = 1);   

utiliser la méthode Equals (en cas de substitution) pour effectuer le test d'égalité.

IsSubsetOf / IsNotSubsetOf

Cette assertion vérifie que le premier paramètre (le sous-ensemble) est contenu dans la collection du deuxième paramètre (le sur-ensemble)..

[TestMethod] public void SubsetTest () string subset = "abc"; string superset = "1d2c3b4a"; CollectionAssert.IsSubsetOf (subset.ToCharArray (), superset.ToCharArray ());  

Notez que le test de sous-ensemble ne teste pas l'ordre ni la séquence. Il vérifie simplement si les éléments de la liste de sous-ensembles sont contenus dans le sur-ensemble..

Assertions de chaîne

Ces méthodes sont implémentées dans la classe Microsoft.VisualStudio.TestTools.UnitTesting.StringAssert:

  • Contient
  • Correspondances / ne correspond pas
  • Commence avec / se termine avec

Ceux-ci sont discutés ensuite.

Contient

La méthode Contains affirme que le sous-ensemble (notez qu'il s'agit du deuxième paramètre) est contenu dans la chaîne (le premier paramètre). Par exemple, ce test réussit:

[TestClass] public class StringTests [TestMethod] public void ContainsTest () string subset = "abc"; string superset = "123abc456"; StringAssert.Contains (sur-ensemble, sous-ensemble);  

Correspondances / ne correspond pas

Cette méthode affirme que la chaîne (le premier paramètre) correspond au modèle de regex fourni dans le deuxième paramètre..

Commence avec / se termine avec

Cette méthode affirme que la chaîne (le premier paramètre) commence ou se termine par une autre chaîne (le deuxième paramètre)..


Exceptions

Les exceptions peuvent être testées sans écrire de blocs try-catch autour de la méthode de test. Par exemple, alors que vous pourriez écrire ceci:

[TestMethod] public void CatchingExceptionsTest () try Divide (5, 0);  catch (ArgumentOutOfRangeException) // Accepte l'exception de manière silencieuse comme valide.  

Il est beaucoup plus lisible d'utiliser l'attribut ExpectedException sur la méthode de test:

[TestMethod] [ExpectedException (typeof (ArgumentOutOfRangeException))] public void BadParameterTest () Divide (5, 0);  

Autres attributs utiles

Certains attributs supplémentaires sont utiles pour exécuter une suite de tests, ainsi que des tests individuels qui améliorent la réutilisabilité et la lisibilité de la base de code de test unitaire..

Configuration / Démontage

Le moteur de test unitaire de Visual Studio fournit quatre attributs de méthode supplémentaires:

  • ClassInitialize
  • ClassCleanup
  • TestInitialize
  • TestCleanup

Ces attributs précèdent et suivent l'exécution de tous les tests de la fixture (classe), ainsi qu'avant et après chaque test de la fixture..

Notez que les méthodes décorées avec cet attribut doivent être statiques.

ClassInitialize

Si une méthode est décorée avec cet attribut, le code de la méthode est exécuté avant l'exécution de tous les tests de la fixture. Notez que cette méthode nécessite un paramètre TestContext.

Cette méthode est utile pour allouer des ressources ou instancier des classes sur lesquelles s'appuient tous les tests de la fixture. Un élément important à prendre en compte pour les ressources et les objets créés lors de l'initialisation de la fixture est que ces ressources et objets doivent être considérés en lecture seule. Il est déconseillé aux tests de modifier l'état des ressources et des objets sur lesquels reposent les autres tests. Cela inclut les connexions à des services tels que des services de base de données et des services Web dont la connexion pourrait être mise dans un état non valide à la suite d'une erreur dans un test, ce qui invaliderait tous les autres tests. De plus, l'ordre d'exécution des tests n'est pas garanti. La modification de l'état d'une ressource et d'un objet créés lors de l'initialisation de la fixture peut entraîner des effets indésirables, en fonction de l'ordre d'exécution des tests..

ClassCleanup

Une méthode décorée avec cet attribut est responsable de la désallocation des ressources, de la fermeture des connexions, etc., créées lors de l'initialisation de la classe. Cette méthode sera toujours exécutée après l'exécution des tests dans le projecteur, indépendamment du succès ou de l'échec des tests eux-mêmes..

TestInitialize

Semblable à l'attribut ClassInitialize, une méthode décorée avec cet attribut sera exécutée pour chaque test avant d'exécuter le test. L'un des objectifs de cet attribut est de s'assurer que les ressources ou les objets alloués par le code ClassInitialize sont initialisés à un état connu avant l'exécution de chaque test..

TestCleanup

En complément de l'attribut TestInitialize, les méthodes décorées avec TestCleanup seront exécutées à la fin. de chaque test.

Configuration et déroulement du démantèlement

Le code suivant illustre le déroulement de la configuration du dispositif, des tests et du démontage en relation avec les tests réels:

[TestClass] classe publique SetupTeardownFlow [[ClassInitialize] publique statique void SetupFixture (contexte TestContext)) Debug.WriteLine ("Fixture Setup.");  [ClassCleanup] public statique statique TeardownFixture () Debug.WriteLine ("Fixture Teardown.");  [TestInitialize] public void SetupTest () Debug.WriteLine ("Test Setup.");  [TestCleanup] public void TeardownTest () Debug.WriteLine ("Test Teardown.");  [TestMethod] public void TestA () Debug.WriteLine ("Test A.");  [TestMethod] public void TestB () Debug.WriteLine ("Test B.");  

L'exécution de cette installation génère la trace de sortie de débogage suivante:

Configuration du luminaire. Configuration de test. Test A. Test de démontage. Configuration de test. Test B. Test de démontage. Démontage du luminaire. 

Comme illustré dans l'exemple précédent, le projecteur est initialisé. Pour chaque test, le code de configuration et de démontage du test s'exécute, suivi du démontage du projecteur à la fin..


Attributs moins fréquemment utilisés

La section suivante décrit les attributs les moins utilisés.

AssemblyInitialize / AssemblyCleanup

Les méthodes décorées avec cet attribut doivent être statiques et sont exécutées lorsque l'assembly est chargé. Cela soulève la question suivante: que se passe-t-il si l'assemblage a plus d'un montage de test?

[TestClass] classe publique Fixture1 [AssemblyInitialize] publique statique void AssemblyInit (contexte TestContext) // ... une opération [TestClass] classe publique Fixture2 [AssemblyInitialize] publique statique void AssemblyInit (contexte TestContext) // ... une opération  

Si vous essayez ceci, le moteur de test ne pourra exécuter aucun test unitaire, en signalant:

"UTA013: UnitTestExamplesVS2008.Fixture2: impossible de définir plusieurs méthodes avec l'attribut AssemblyInitialize dans un assembly."

Par conséquent, une seule méthode AssemblyInitialize et une seule méthode AssemblyCleanup peuvent exister pour un assembly, quel que soit le nombre de fixtures de test dans cet assembly. Il est donc recommandé de ne placer aucun test dans la classe définissant ces méthodes:

[TestClass] public class AssemblyFixture [AssemblyInitialize] public statique void AssemblySetup (contexte TestContext) Debug.WriteLine ("Assembly Initialize.");  [AssemblyCleanup] public statique statique AssemblyTeardown () Debug.WriteLine ("Assembly Cleanup.");  

entraînant la séquence d'exécution suivante:

Assemblage Initialize. Configuration du luminaire. Configuration de test. Test A. Test de démontage. Configuration de test. Test B. Test de démontage. Démontage de l'appareil. Nettoyage d'assemblage. 

Notez les appels supplémentaires d'initialisation et de nettoyage d'assembly..

Ignorer

Cette méthode peut décorer des méthodes spécifiques ou des appareils complets.

Ignorer une méthode de test

Si cet attribut décore une méthode de test:

[TestMethod, Ignore] public void TestA () Debug.WriteLine ("Test A.");  

le test ne fonctionnera pas. Malheureusement, le volet Résultats du test Visual Studio n'indique pas que des tests sont actuellement ignorés:

Les tests ignorés ne sont pas affichés

Comparez cela avec NUnit, qui montre clairement les tests ignorés:

NUnit affiche les tests ignorés

L'écran NUnit marque l'ensemble de l'arborescence de test comme étant «inconnue» lorsqu'une ou plusieurs méthodes de test sont marquées comme «Ignorer».

Ignorer un appareil de test

Les méthodes d'un appareil complet peuvent être ignorées en utilisant l'attribut Ignore au niveau de la classe:

[TestClass, Ignore], classe publique SetupTeardownFlow … etc… 

Effacement du cache de test

Si vous ajoutez l'attribut Ignore à une méthode, vous remarquerez peut-être que Visual Studio exécute toujours le test. Il est nécessaire d'effacer le cache de test pour Visual Studio pour prendre en compte les modifications. Une façon de faire est de nettoyer la solution et de la reconstruire..

Propriétaire

Utilisé à des fins de rapport, cet attribut décrit la personne responsable de la méthode de test unitaire..

DeploymentItem

Si les tests unitaires sont exécutés dans un dossier de déploiement séparé, cet attribut peut être utilisé pour spécifier les fichiers nécessaires à l'exécution d'une classe ou d'une méthode de test. Vous pouvez spécifier des fichiers ou des dossiers à copier dans le dossier de déploiement et éventuellement spécifier le chemin cible par rapport au dossier de déploiement..

La description

Utilisé pour les rapports, cet attribut fournit une description de la méthode de test. Bizarrement, cet attribut est disponible uniquement sur les méthodes de test et n'est pas disponible sur les classes de test..

Type d'hôte

Pour les méthodes de test, cet attribut est utilisé pour spécifier l'hôte sur lequel le test unitaire sera exécuté..

Priorité

Cet attribut n'est pas utilisé par le moteur de test, mais peut l'être, par réflexion, par votre propre code de test. L'utilité de cet attribut est discutable.

WorkItem

Si vous utilisez Team Foundation Server (TFS), vous pouvez utiliser cet attribut sur une méthode de test pour spécifier l'ID d'élément de travail attribué par TFS au test d'unité spécifique..

CssIteration / CssProjectStructure

Ces deux attributs sont utilisés en relation avec TeamBuild et TestManagementService et vous permettent de spécifier une itération de projet à laquelle la méthode de test correspond..


Test paramétré avec l'attribut DataSource

Le moteur de test unitaire de Microsoft prend en charge les sources de données CSV, XML ou de base de données pour les tests paramétrés. Il ne s'agit pas exactement de tests paramétrés véritables (voir comment NUnit implémente les tests paramétrés), car les paramètres ne sont pas transmis à la méthode de test unitaire, mais doivent être extraits de la source de données et transmis à la méthode testée. Cependant, la possibilité de charger des données de test dans un DataTable à partir de diverses sources est utile pour piloter l'automatisation de test..

Source de données CSV

Un fichier texte séparé par des virgules peut être utilisé pour une source de données:

Numérateur, dénominateur, attendu Résultats 10, 5, 2 20,5, 4 33, 3, 11 

et utilisé dans une méthode d'essai:

[TestClass] public class DataSourceExamples public TestContext TestContext get; ensemble;  [TestMethod] [DataSource ("Microsoft.VisualStudio.TestTools.DataSource.CSV", "C: \\ temp \\ csvData.txt", "csvData # txt", DataAccessMethod.Sequential)] public void CsvDataSourceTest () int n = Convert.ToInt32 (TestContext.DataRow ["Numérateur"]); int d = Convert.ToInt32 (TestContext.DataRow ["Dénominateur"]); int r = Convert.ToInt32 (TestContext.DataRow ["ExpectedResult"]); Debug.WriteLine ("n =" + n + ", d =" + d + ", r =" + r);  

Il en résulte le résultat suivant:

n = 10, d = 5, r = 2 n = 20, d = 5, r = 4 n = 33, d = 3, r = 11 

Notez que la fenêtre de résultat du test n’affiche pas le paramètre exécuté (comparez cela à NUnit):

Résultats de test paramétrés

Cependant, il est avantageux de ne pas afficher chaque combinaison de tests, en particulier pour les grands ensembles de données..

Source de données XML

Étant donné un fichier XML tel que:

    

Voici un exemple d'utilisation d'une source de données XML pour un test unitaire:

[TestMethod] [DataSource ("Microsoft.VisualStudio.TestTools.DataSource.XML", "C: \\ temp \\ xmlData.xml", "Row", DataAccessMethod.Sequential)] public void XmlDataSourceTest () (int). .ToInt32 (TestContext.DataRow ["Numérateur"]); int d = Convert.ToInt32 (TestContext.DataRow ["Dénominateur"]); int r = Convert.ToInt32 (TestContext.DataRow ["ExpectedResult"]); Debug.WriteLine ("n =" + n + ", d =" + d + ", r =" + r);  

Notez que, à l'exception des paramètres d'attribut de source de données, le code de test est le même..

Base de données de la base de données

Une table de base de données peut également être utilisée comme source de données. Étant donné un tableau tel que:

Table de base de données en tant que source de données

et données:

Base de données de test

Un exemple de méthode de test utilisant ces données ressemble à ceci:

[TestMethod] [DataSource ("System.Data.SqlClient", "Source de données = INTERACX-HP; Catalogue initial = UnitTesting; Sécurité intégrée = True", "DivideTestData", DataAccessMethod.Sequential)] public void XmlDataSourceTest () int n = Convert.ToInt32 (TestContext.DataRow ["Numérateur"]); int d = Convert.ToInt32 (TestContext.DataRow ["Dénominateur"]); int r = Convert.ToInt32 (TestContext.DataRow ["ExpectedResult"]); Debug.WriteLine ("n =" + n + ", d =" + d + ", r =" + r);  

Encore une fois, observez que le code de la méthode de test est identique, la seule chose que nous ayons faite ici est de changer la définition de DataSource.

Attribut TestProperty

La documentation MSDN relative à cet attribut illustre la déclaration d'une paire nom-valeur TestProperty puis l'acquisition, à l'aide de la réflexion, du nom et de la valeur. Cela semble être un moyen obtus de créer des tests paramétrés. 

De plus, le code, décrit sur le blog de Craig Andera, permettant d'utiliser l'attribut TestProperty pour paramétrer le processus d'initialisation du test n'affecte pas la collection TestContext.Properties sous Visual Studio 2008 ou Visual Studio 2012..