Procédure pas à pas : création et exécution de tests unitaires pour le code managé

Cette procédure pas-à-pas vous accompagne lors de la création, l'exécution, et la personnalisation d'une série de tests unitaires à l'aide de l'infrastructure de test unitaire Microsoft pour le code managé et l'Explorateur de Tests de Visual Studio.Vous commencez avec un projet C# qui est en développement, vous créez des tests qui exercent son code, vous exécutez les tests et vous examinez les résultats.Ensuite, vous pouvez modifier votre code de projet et réexécuter les tests.

Cette rubrique contient les sections suivantes :

Préparation de la procédure pas-à-pas

Créer un projet de test unitaire

Créez la classe de test

Créez la première méthode de test

Générer et exécuter le test.

Vérifiez votre code et ré exécutez vos tests

Utilisez les tests unitaires pour améliorer votre code

[!REMARQUE]

Cette procédure pas-à-pas utilise l'infrastructure de test unitaire Microsoft pour le code managé.L'Explorateur de Tests peut également exécuter des tests des infrastructures de test unitaire tiers qui ont des adaptateurs pour l'Explorateur de Tests.Pour plus d'informations, consultez Comment : installer des infrastructures de tests unitaires tiers

[!REMARQUE]

Pour plus d'informations sur la façon d'exécuter des tests à partir d'une ligne de commande, consultez Procédure pas à pas : utilisation de l'utilitaire de test de ligne de commande.

Composants requis

Préparation de la procédure pas-à-pas

Pour préparer la procédure pas à pas

  1. Ouvrez Visual Studio 2012.

  2. Dans le menu Fichier, pointez sur Nouveau, puis cliquez sur Projet.

    La boîte de dialogue Nouveau projet s'affiche.

  3. Sous Modèles installés, cliquez sur Visual C#.

  4. Dans la liste de types d'applications, cliquez sur Bibliothèque de classes.

  5. Dans la zone Nom, tapez Banque, puis cliquez sur OK.

    [!REMARQUE]

    Si le nom « Bank » est déjà utilisé, choisissez un autre nom pour le projet.

    Le nouveau projet Banque est créé et affiché dans l'Explorateur de solutions, avec le fichier Class1.cs ouvert dans l'éditeur de code.

    [!REMARQUE]

    Si le fichier Class1.cs n'est pas ouvert dans l'éditeur de code, double-cliquez sur ce fichier dans l'Explorateur de solutions pour l'ouvrir.

  6. Copiez le code source de l'Exemple de projet pour la création de tests unitaires.

  7. Remplacez le contenu d'origine de Class1.cs par le code de l'Exemple de projet pour la création de tests unitaires.

  8. Enregistrez le fichier en tant que BankAccount.cs

  9. Dans le menu Générer, cliquez sur Générer la solution.

Vous avez maintenant un projet nommé Bank.Il contient le code source à tester et des outils avec lesquels le tester.L'espace de noms pour Bank, BankAccountNS, contient la classe publique BankAccount, dont vous testerez les méthodes dans les procédures suivantes.

Dans ce démarrage rapide, nous nous concentrons sur la méthode Debit . La méthode Debit est appelée lorsque de l'argent est retiré d'un compte et contient le code suivant :

// method under test
public void Debit(double amount)
{
    if(amount > m_balance)
    {
        throw new ArgumentOutOfRangeException("amount");
    }
    if (amount < 0)
    {
        throw new ArgumentOutOfRangeException("amount");
    }
    m_balance += amount;
}

Créer un projet de test unitaire

Condition préalable : suivez les étapes de la procédure intitulée Préparation de la procédure pas à pas.

Pour créer un projet de test unitaire

  1. Dans le menu Fichier, cliquez sur Ajouter, puis sur Nouveau Projet....

  2. Dans la boîte de dialogue Nouveau Projet, développez Installé, développez Visual C#, puis choisissez Test.

  3. Depuis la liste des modèles, sélectionnez Projet de Test Unitaire.

  4. Dans la zone Nom , entrez BankTest, puis choisissez OK.

    Le projet BankTests est ajouté à la solution Bank .

  5. Dans le projet BankTests , ajoutez une référence à la solution Bank .

    Dans l'Explorateur de Solutions, sélectionnez Références dans le projet BankTests puis sélectionnez Ajouter une Référence... à partir du menu contextuel.

  6. Dans la boîte de dialogue Gestionnaire de Référence, développez Solution puis cochez l'élément Bank .

Créez la classe de test

Nous avons besoin d'une classe de test pour vérifier la classe BankAccount .Nous pouvons utiliser UnitTest1.cs qui a été généré par le modèle de projet, mais nous devrions donner au fichier et à la classe des noms plus descriptifs.Nous pouvons faire cela en une seule étape en renommant le fichier dans l'Explorateur de Solutions.

Renommer un fichier de classe

Dans l'Explorateur de Solutions, sélectionnez le fichier UnitTest1.cs dans le projet BankTests.Depuis le menu contextuel, choisissez Renommer, puis renommez le fichier à BankAccountTests.cs.Choisissez Oui dans la boîte de dialogue qui demande si vous souhaitez renommer toutes les références à l'élément de code "UnitTest1" dans le projet.Cette étape a pour effet de remplacer le nom de la classe par BankAccountTest.

Le fichier BankAccountTests.cs contient maintenant le code suivant :

// unit test code
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace BankTests
{
    [TestClass]
    public class BankAccountTests
    {
        [TestMethod]
        public void TestMethod1()
        {
        }
    }
}

Ajoutez une instruction using au projet en test

Nous pouvons également ajouter une instruction using à la classe pour vous permettre d'appeler dans le projet en test sans utiliser des noms complets qualifiés.En haut du fichier de classe, ajoutez :

using BankAccountNS

ms182532.collapse_all(fr-fr,VS.110).gifTestez les spécifications de classe

La configuration minimale requise pour une classe de test est la suivante :

  • L'attribut [TestClass] est requis dans l'infrastructure de test unitaire Microsoft pour le code managé pour toute classe qui contient les méthodes de test unitaire que vous souhaitez exécuter dans l'Explorateur de Tests.

  • Chaque méthode de test que vous souhaitez l'explorateur de tests pour exécuter doit avoir l'attribut d' [TestMethod].

Vous pouvez avoir d'autres classes dans un projet de test unitaire qui n'ont pas l'attribut d' [TestClass], et vous pouvez avoir d'autres méthodes dans les classes de test qui n'ont pas l'attribut d' [TestMethod] .Vous pouvez utiliser ces autres classes et méthodes dans vos méthodes de test.

Créez la première méthode de test

Dans cette procédure, nous écrirons des méthodes de test unitaire pour vérifier le comportement de la méthode Debit de la classe BankAccount .La méthode est répertoriée ci-dessus.

En analysant la méthode testée, nous déterminons qu'il existe au moins trois comportements qui doivent être vérifiés :

  1. La méthode lève un [ArgumentOutOfRangeException] si la quantité de crédit est supérieure au solde.

  2. Elle lève également ArgumentOutOfRangeException si la quantité de crédit est inférieure à zéro.

  3. Si les contrôles en 1.) et 2.) sont satisfaisants, la méthode soustrait la quantité du solde de compte.

Dans notre premier test, nous vérifions qu'une quantité valide (qui est inférieure au solde de compte et supérieure à zéro) supprime la quantité appropriée du compte.

Pour créer une méthode de test

  1. Ajoutez une instruction using BankAccountNS; au fichier BankAccountTests.cs.

  2. Ajoutez la méthode suivante à cette classe BankAccountTests :

    // unit test code
    [TestMethod]
    public void Debit_WithValidAmount_UpdatesBalance()
    {
        // arrange
        double beginningBalance = 11.99;
        double debitAmount = 4.55;
        double expected = 7.44;
        BankAccount account = new BankAccount("Mr. Bryan Walton", beginningBalance);
    
        // act
        account.Debit(debitAmount);
    
        // assert
        double actual = account.Balance;
        Assert.AreEqual(expected, actual, 0.001, "Account not debited correctly");
    }
    

La méthode est plutôt simple.Nous mettons en place un nouvel objet BankAccount avec un solde de début puis nous retirons une quantité valide.Nous utilisons la méthode de l'infrastructure de test unitaire Microsoft pour le code managé AreEqual pour vérifier que est le solde de fin correspond à ce que nous attendions.

ms182532.collapse_all(fr-fr,VS.110).gifSpécifications des méthodes de test

Une méthode de test doit répondre aux conditions suivantes :

  • La méthode doit être décorée avec l'attribut [TestMethod].

  • La méthode doit retourner void.

  • La méthode ne peut avoir de paramètres.

Générer et exécuter le test.

Pour générer et exécuter le test

  1. Dans le menu Générer, choisissez Générer la solution.

    S'il n'y a pas d'erreurs, la fenêtre UnitTestExplorer apparaît avec Debit_WithValidAmount_UpdatesBalance listé dans le groupe Tests Non Exécutés .Si l'Explorateur de Tests n'apparaît pas après une génération réussie, choisissez Test dans le menu, puis choisissez Fenêtres, puis choisissez Explorateur de Tests.

  2. Choisissez Exécuter Tout pour exécuter le test.Pendant que le test s'exécute la barre d'état en haut de la fenêtre s'anime.À l'issue de la série de tests, la barre devient verte si toutes les méthodes de test ont réussi, ou rouge si l'un des tests a échoué.

  3. Dans ce cas, le test échoue.La méthode de test est déplacée vers Tests Échoués.groupe spécifique.Sélectionnez la méthode dans l'Explorateur de Tests pour en afficher les détails en bas de la fenêtre.

Vérifiez votre code et ré exécutez vos tests

Analyse des résultats des tests

Le résultat de test contient un message qui décrit l'échec.Pour la méthode AreEquals , un message vous affiche ce qui était attendu (le paramètreExpected<XXX>) et ce qui a été reçu réellement (le paramètre Actual<YYY> ).Nous nous attendions à ce que le solde décline par rapport au solde de début, mais il a plutôt été augmenté de la quantité du retrait.

Un réexamen du code de Debit indique que le test unitaire a réussi à trouver un bogue.La quantité du retrait est ajoutée au solde de compte lorsqu'elle doit être soustraite.

Corrigez le bogue

Pour corriger l'erreur, remplacez simplement la ligne

m_balance += amount;

par

m_balance -= amount;

Ré exécutez le test.

Dans l'Explorateur de Tests, sélectionnez Exécuter Tout pour ré exécuter le test.La barre rouge/verte devient verte, et le test est déplacé dans le groupe Tests Réussis .

Utilisez les tests unitaires pour améliorer votre code

Cette section décrit comment un processus itératif d'analyse, de développement de test unitaire, et de refactorisation peut vous aider à rendre votre code de production plus fiable et efficace.

Analyse des problèmes

Après avoir créé une méthode de test pour vérifier qu'une quantité valide est correctement déduite dans la méthode Debit , nous pouvons nous tourner vers les cas restants dans notre analyse d'origine :

  1. La méthode lève un ArgumentOutOfRangeException si la quantité de crédit est supérieure au solde.

  2. Elle lève également ArgumentOutOfRangeException si la quantité de crédit est inférieure à zéro.

Créez les méthodes de test

Une première tentative de création d'une méthode de test pour résoudre ces problèmes semble prometteuse :

//unit test method
[TestMethod]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void Debit_WhenAmountIsLessThanZero_ShouldThrowArgumentOutOfRange()
{
    // arrange
    double beginningBalance = 11.99;
    double debitAmount = -100.00;
    BankAccount account = new BankAccount("Mr. Bryan Walton", beginningBalance);

    // act
    account.Debit(debitAmount);

    // assert is handled by ExpectedException
}

Nous utilisons l'attribut ExpectedExceptionAttribute pour déclarer que la bonne exception a été levée.L'attribut entraîne l'échec du test à moins qu'une ArgumentOutOfRangeException soit levée.Exécuter le test avec à la fois les valeurs debitAmount positives et négatives puis modifier temporairement la méthode testée pour lever une ApplicationException générique lorsque la quantité est inférieure à zéro indique que le test se comporte correctement.Pour tester le cas lorsque la quantité retirée est supérieure au solde, tout ce que nous devons effectuer est :

  1. Créez une nouvelle méthode de test nommée Debit_WhenAmountIsMoreThanBalance_ShouldThrowArgumentOutOfRange.

  2. Copiez le corps de la méthode depuis Debit_WhenAmountIsLessThanZero_ShouldThrowArgumentOutOfRange vers la nouvelle méthode.

  3. Définissez debitAmount à un nombre supérieur au solde.

Exécutez les tests

Exécuter les deux méthodes avec des valeurs différentes pour debitAmount montre que les tests gèrent correctement nos cas restants.Exécuter les trois tests confirme que tous les cas dans notre analyse d'origine sont traités correctement.

Poursuivez l'analyse

Toutefois, les deux dernières méthodes de test sont également quelque peu troublantes.Nous ne pouvons pas être certains de quelle condition dans le code en test se déclenche lorsque l'un ou l'autre des tests est exécuté.Une façon de faire la différence entre les deux conditions serait utile.Comme nous pensons davantage au problème, il devient clair que connaître quelle condition a été violée augmenterait notre confiance dans les tests.Ces informations seraient également très probablement utiles au mécanisme de production qui gère l'exception lorsqu'elle est levée par la méthode testée.Générer plus d'informations lorsque la méthode se déclenche aiderait tout ce qui est concerné, mais l'attribut ExpectedException ne peut pas fournir ces informations.

En examinant encore la méthode testée, nous constatons que les deux instructions conditionnelles utilisent un constructeur ArgumentOutOfRangeException qui prend le nom de l'argument comme paramètre :

throw new ArgumentOutOfRangeException("amount");

À partir d'une recherche sur MSDN Library, nous découvrons qu'un constructeur qui rapporte des informations bien plus riches existe.ArgumentOutOfRangeException(String, Object, String) inclut le nom de l'argument, la valeur de l'argument, et un message défini par l'utilisateur.Nous pouvons refactoriser la méthode testée pour utiliser ce constructeur.Encore mieux, nous pouvons utiliser les membres de type disponibles publiquement pour spécifier les erreurs.

Refactorisez le code en test

Nous définissons d'abord deux constantes pour les messages d'erreur à la portée de classe :

// class under test
public const string DebitAmountExceedsBalanceMessage = "Debit amount exceeds balance";
public const string DebitAmountLessThanZeroMessage = "Debit amount less than zero";

Nous modifions ensuite les deux instructions conditionnelles dans la méthode Debit :

// method under test
// ...
    if (amount > m_balance)
    {
        throw new ArgumentOutOfRangeException("amount", amount, DebitAmountExceedsBalanceMessage);
    }

    if (amount < 0)
    {
        throw new ArgumentOutOfRangeException("amount", amount, DebitAmountLessThanZeroMessage);
    }
// ...

Refactoriser les méthodes de test

Dans notre méthode de test, nous retirons d'abord l'attribut ExpectedException .À sa place, nous interceptons l'exception levée et vérifions qu'elle a été levée dans l'instruction de condition appropriée.Toutefois, nous devons maintenant choisir entre deux options pour vérifier nos conditions restantes.Par exemple dans la méthode Debit_WhenAmountIsMoreThanBalance_ShouldThrowArgumentOutOfRange , nous pouvons prendre l'une des actions suivantes :

  • Déclarez que la propriété ActualValue de l'exception (le deuxième paramètre du constructeur de ArgumentOutOfRangeException ) est supérieure au solde de début.Cette option requiert que nous testions la propriété ActualValue de l'exception sur la variable beginningBalance de la méthode de test, et requiert également de vérifier alors que ActualValue est supérieure à zéro.

  • Déclarez que le message (le troisième paramètre du constructeur) inclut le DebitAmountExceedsBalanceMessage défini dans la classe BankAccount .

La méthode StringAssert.Contains dans l'infrastructure de test unitaire Microsoft nous permet de vérifier la deuxième option sans les calculs requis par la première option.

Une deuxième tentative de modifier Debit_WhenAmountIsMoreThanBalance_ShouldThrowArgumentOutOfRange pourrait donner :

[TestMethod]
public void Debit_WhenAmountIsGreaterThanBalance_ShouldThrowArgumentOutOfRange()
{
    // arrange
    double beginningBalance = 11.99;
    double debitAmount = 20.0;
    BankAccount account = new BankAccount("Mr. Bryan Walton", beginningBalance);\

    // act
    try
    {
        account.Debit(debitAmount);
    }
    catch (ArgumentOutOfRangeException e)
    {
        // assert
        StringAssert.Contains(e.Message, BankAccount. DebitAmountExceedsBalanceMessage);
    }
}

Re-tester, réécrire, et réanalyser

Lorsque nous retestons les méthodes de test avec des valeurs différentes, nous rencontrons les faits suivants :

  1. Si nous capturons l'erreur correcte à l'aide d'un debitAmount supérieur au solde, l'assertion Contains passe, l'exception est ignorée, et ainsi la méthode de test passe.Il s'agit du comportement que nous souhaitons.

  2. Si nous utilisons un debitAmount, l'assertion échoue parce qu'un mauvais message d'erreur est retourné.L'assertion échoue également si nous introduisons une exception temporaire ArgumentOutOfRange à un autre point dans le chemin de code de la méthode testée.Cela aussi est bon.

  3. Si la valeur d' debitAmount est valide (c. autrement dit., sauf si le solde mais supérieur à zéro, aucune exception n'est interceptée, l'assertion n'est jamais interceptée.La méthode de test est réussie.Ce n'est pas bien, car nous souhaitons que la méthode de test échoue si aucune exception n'est levée.

Le troisième fait est un bogue dans notre méthode de test.Pour essayer de résoudre le problème, nous ajoutons une assertion Fail à la fin de la méthode de test pour gérer le cas où aucune exception n'est levée.

Mais retester montre que le test échoue maintenant si l'exception correcte est interceptée.L'instruction catch réinitialise l'exception et la méthode continue à s'exécuter, et échoue sur la nouvelle assertion.Pour résoudre le nouveau problème, nous ajoutons une instruction return après StringAssert.Retester confirme que nous avons résolu nos problèmes.Notre version finale de l' Debit_WhenAmountIsMoreThanBalance_ShouldThrowArgumentOutOfRange ressemble à ce qui suit :

[TestMethod]
public void Debit_WhenAmountIsGreaterThanBalance_ShouldThrowArgumentOutOfRange()
{
    // arrange
    double beginningBalance = 11.99;
    double debitAmount = 20.0;
    BankAccount account = new BankAccount("Mr. Bryan Walton", beginningBalance);\

    // act
    try
    {
        account.Debit(debitAmount);
    }
    catch (ArgumentOutOfRangeException e)
    {
        // assert
        StringAssert.Contains(e.Message, BankAccount. DebitAmountExceedsBalanceMessage);
        return;
    }
    Assert.Fail("No exception was thrown.")
}

Dans cette section finale, le travail que nous avons fait en améliorant notre code de test a conduit à des méthodes de test plus fiables et plus instructives.Mais plus important encore, l'analyse supplémentaire a également conduit à du meilleur code dans notre projet en test.