Partage via


Procédure pas à pas : créer et exécuter des tests unitaires pour le code managé

Cet article vous guide tout au long de la création, de l’exécution et de la personnalisation d’une série de tests unitaires à l’aide de l’infrastructure de test Microsoft pour le code managé et Visual Studio l’Explorateur de tests. Vous commencez par un projet C# qui est en cours de développement, créez des tests qui exercicent son code, exécutez les tests et examinez les résultats. Ensuite, vous modifiez le code du projet et réexécutez les tests. Si vous souhaitez une vue d’ensemble conceptuelle de ces tâches avant d’effectuer ces étapes, consultez concepts de base des tests unitaires. Si vous souhaitez générer automatiquement des tests à partir du code existant, consultez Créer des stubs de méthode de test unitaire à partir du code.

Créer un projet à tester

  1. Ouvrez Visual Studio.

  2. Dans la fenêtre de démarrage, choisissez Créer un projet.

  3. Recherchez et sélectionnez le modèle de projet C# Application console pour .NET, puis cliquez sur Suivant.

    Remarque

    Si vous ne voyez pas le modèle application console, vous pouvez l’installer à partir de la fenêtre Créer un projet. Dans le message Vous ne trouvez pas ce que vous cherchez ?, choisissez le lien Installer d’autres outils et fonctionnalités. Dans le Visual Studio Installer, choisissez ensuite la charge de travail Développement .NET Desktop.

  4. Nommez le projet Bank, puis cliquez sur Suivant.

    Choisissez le framework cible recommandé ou .NET 8, puis choisissez Créer.

    Le projet Bank est créé et affiché dans l’Explorateur de solutions avec le fichier Program.cs ouvert dans l’éditeur de code.

    Remarque

    Si Program.cs n’est pas ouvert dans l’éditeur, double-cliquez sur le fichier Program.cs dans l’Explorateur de solutions pour l’ouvrir.

  5. Remplacez le contenu de Program.cs par le code C# suivant qui définit une classe, BankAccount:

    using System;
    
    namespace BankAccountNS
    {
        /// <summary>
        /// Bank account demo class.
        /// </summary>
        public class BankAccount
        {
            private readonly string m_customerName;
            private double m_balance;
    
            private BankAccount() { }
    
            public BankAccount(string customerName, double balance)
            {
                m_customerName = customerName;
                m_balance = balance;
            }
    
            public string CustomerName
            {
                get { return m_customerName; }
            }
    
            public double Balance
            {
                get { return m_balance; }
            }
    
            public void Debit(double amount)
            {
                if (amount > m_balance)
                {
                    throw new ArgumentOutOfRangeException("amount");
                }
    
                if (amount < 0)
                {
                    throw new ArgumentOutOfRangeException("amount");
                }
    
                m_balance += amount; // intentionally incorrect code
            }
    
            public void Credit(double amount)
            {
                if (amount < 0)
                {
                    throw new ArgumentOutOfRangeException("amount");
                }
    
                m_balance += amount;
            }
    
            public static void Main()
            {
                BankAccount ba = new BankAccount("Mr. Bryan Walton", 11.99);
    
                ba.Credit(5.77);
                ba.Debit(11.22);
                Console.WriteLine("Current balance is ${0}", ba.Balance);
            }
        }
    }
    
  6. Renommez le fichier BankAccount.cs en effectuant un clic droit et en choisissant Renommer dans l’Explorateur de solutions.

  7. Dans le menu Générer, cliquez sur Générer la solution (ou appuyez sur Ctrl + SHIFT + B).

Vous disposez maintenant d’un projet avec des méthodes que vous pouvez tester. Dans cet article, les tests se concentrent sur la méthode Debit. La méthode Debit est appelée lorsque l’argent est retiré d’un compte.

Créer un projet de test unitaire

  1. Dans le menu Fichier, sélectionnez Ajouter>Nouveau projet.

    Conseil

    Vous pouvez également cliquer avec le bouton droit sur la solution dans Explorateur de solutions et choisir Ajouter>nouveau projet.

  2. Saisissez test dans la zone de recherche, sélectionnez C# comme langue, puis sélectionnez le projet de test C# MSTest pour le modèle .NET, puis cliquez sur Suivant.

    Remarque

    Dans Visual Studio 2019 version 16.9, le modèle de projet MSTest est projet de test unitaire.

  3. Nommez le projet BankTests et cliquez sur Suivant.

  4. Choisissez le framework cible recommandé ou .NET 8, puis choisissez Créer.

    À compter de Visual Studio 2022 version 17.10, vous pouvez également sélectionner un exécuteur de test. En ce qui concerne le Test Runner, vous pouvez choisir VSTest ou MSTest. Pour plus d’informations sur la différence entre les test runners, consultez comparaison Microsoft.Testing.Platform et VSTest.

    Le projet BankTests est ajouté à la solution Bank.

  5. Dans le projet BankTests, ajoutez une référence au projet Bank.

    Dans l’Explorateur de solutions , sélectionnez Dépendances sous le projet BankTests, puis choisissez l'option Ajouter une référence (ou Ajouter une référence de projet) dans le menu contextuel.

  6. Dans la boîte de dialogue gestionnaire de références, développez Projets, sélectionnez Solution, puis cochez l’élément Banque.

  7. Choisissez OK.

Créer la classe de test

Créez une classe de test pour vérifier la classe BankAccount. Vous pouvez utiliser le fichier UnitTest1.cs généré par le modèle de projet, mais donner au fichier et à la classe des noms plus descriptifs.

Renommer un fichier et une classe

  1. Pour renommer le fichier, dans Explorateur de solutions, sélectionnez le fichier UnitTest1.cs dans le projet BankTests. Dans le menu contextuel, choisissez Renommer (ou appuyez sur F2), puis renommez le fichier en BankAccountTests.cs.

  2. Pour renommer la classe, positionnez le curseur sur UnitTest1 dans l’éditeur de code, cliquez avec le bouton droit, puis choisissez Renommer (ou appuyez sur F2). Saisissez BankAccountTests, puis appuyez sur Entrée.

Le fichier BankAccountTests.cs contient désormais le code suivant :

// The 'using' statement for Test Tools is in GlobalUsings.cs
// using Microsoft.VisualStudio.TestTools.UnitTesting;

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

Ajoutez une instruction using

Ajoutez une usinginstruction à la classe de test pour pouvoir appeler le projet testé sans utiliser de noms complets. En haut du fichier de classe, ajoutez :

using BankAccountNS;

Conditions requises pour la classe de test

Les exigences minimales pour une classe de test sont les suivantes :

  • L’attribut [TestClass] est requis sur n’importe quelle classe qui contient des méthodes de test unitaire que vous souhaitez exécuter dans l’Explorateur de tests.

  • Chaque méthode de test que vous souhaitez que l’Explorateur de tests reconnaisse doit avoir l’attribut [TestMethod].

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

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

Dans cette procédure, vous écrivez des méthodes de test unitaire pour vérifier le comportement de la méthode Debit de la classe BankAccount.

Il existe au moins trois comportements à vérifier :

  • La méthode lève une ArgumentOutOfRangeException si le montant du débit est supérieur au solde.

  • La méthode génère une ArgumentOutOfRangeException si le montant du débit est inférieur à zéro.

  • Si le montant du débit est valide, la méthode soustrait le montant du débit du solde du compte.

Conseil

Vous pouvez supprimer la méthode TestMethod1 par défaut, car vous ne l’utiliserez pas dans cette procédure pas à pas.

Pour créer une méthode de test

Le premier test vérifie qu’un montant valide (c’est-à-dire inférieur au solde du compte et supérieur à zéro) retire le montant correct du compte. Ajoutez la méthode suivante à cette classe BankAccountTests :

[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 simple : elle configure un nouvel objet BankAccount avec un solde de début, puis retire un montant valide. Il utilise la méthode Assert.AreEqual pour vérifier que le solde de fin est conforme aux attentes. Les méthodes telles que Assert.AreEqual, Assert.IsTrueet d’autres sont fréquemment utilisées dans les tests unitaires. Pour plus d’informations conceptuelles sur l’écriture d’un test unitaire, consultez Écrire vos tests.

Conditions requises pour les méthodes de test

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

  • Il est décoré avec l’attribut [TestMethod].

  • Il retourne void.

  • Il ne peut pas avoir de paramètres.

Générer et exécuter le test

  1. Dans le menu Générer, choisissez sur Générer la solution (ou appuyez sur Ctrl + SHIFT + B).

  2. Si l’Explorateur de tests n’est pas ouvert, ouvrez-le en choisissant Test>Explorateur de tests (ou Test>Windows>Explorateur de tests) dans la barre de menus supérieure (ou appuyez sur Ctrl + E, T).

  3. Choisissez Exécuter tout pour exécuter le test (ou appuyez sur Ctrl + R, V).

    Pendant que le test est en cours d’exécution, la barre d’état en haut de la fenêtre de l’Explorateur de tests est animée. À la fin de la série de tests, la barre devient verte si toutes les méthodes de test réussissent, ou rouge si l’un des tests échoue.

    Dans ce cas, le test échoue.

  4. Sélectionnez la méthode dans Explorateur de tests pour afficher les détails en bas de la fenêtre.

Corriger votre code et réexécuter vos tests

Le résultat du test contient un message qui décrit l’échec. Vous devrez peut-être descendre dans la hiérarchie pour voir ce message. Pour la méthode AreEqual, le message affiche ce qui était attendu et ce qui a été réellement reçu. Vous vous attendiez à ce que le solde diminue, mais au lieu de cela, il a augmenté par le montant du retrait.

Le test unitaire a découvert un bogue : le montant du retrait est ajouté au solde du compte quand il doit être soustrait.

Corriger le bug

Pour corriger l’erreur, dans le fichier BankAccount.cs, remplacez la ligne :

m_balance += amount;

avec :

m_balance -= amount;

Réexécuter le test

Dans Explorateur de tests, choisissez Exécuter tout pour réexécuter le test (ou appuyez sur Ctrl + R, V). La barre rouge/verte devient verte pour indiquer que le test a réussi.

Explorateur de tests dans Visual Studio 2019 montrant le test réussi

Explorateur de tests dans Visual Studio 2019 montrant le test réussi

Utiliser des tests unitaires pour améliorer votre code

Cette section décrit comment un processus itératif d’analyse, de développement de tests unitaires et de refactorisation peut vous aider à rendre votre code de production plus robuste et plus efficace.

Analyser les problèmes

Vous avez créé une méthode de test pour confirmer qu’un montant valide est correctement déduit dans la méthode Debit. À présent, vérifiez que la méthode lève une exception ArgumentOutOfRangeException si le montant du débit est :

  • supérieur à l’équilibre, ou
  • inférieur à zéro.

Créer et exécuter de nouvelles méthodes de test

Créez une méthode de test pour vérifier le comportement correct lorsque le montant du débit est inférieur à zéro :

[TestMethod]
public void Debit_WhenAmountIsLessThanZero_ShouldThrowArgumentOutOfRange()
{
    // Arrange
    double beginningBalance = 11.99;
    double debitAmount = -100.00;
    BankAccount account = new BankAccount("Mr. Bryan Walton", beginningBalance);

    // Act and assert
    Assert.ThrowsException<System.ArgumentOutOfRangeException>(() => account.Debit(debitAmount));
}

Utilisez la méthode ThrowsException pour affirmer que l’exception correcte a été levée. Cette méthode entraîne l’échec du test, sauf si une ArgumentOutOfRangeException est lancée. Si vous modifiez temporairement la méthode testée pour lever une ApplicationException plus générique lorsque le montant du débit est inférieur à zéro, le test se comporte correctement, c’est-à-dire qu’il échoue.

Pour tester le cas lorsque le montant retiré est supérieur au solde, procédez comme suit :

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

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

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

Exécutez les deux tests et vérifiez qu’ils réussissent.

Poursuivre l’analyse

La méthode testée peut être améliorée. Avec l’implémentation actuelle, nous n’avons aucun moyen de savoir quelle condition (amount > m_balance ou amount < 0) a conduit à l’exception levée pendant le test. Nous savons simplement qu’un ArgumentOutOfRangeException a été généré quelque part dans la méthode. Il serait préférable de savoir quelle condition dans BankAccount.Debit a provoqué la levée de l’exception (amount > m_balance ou amount < 0) afin que nous puissions être sûrs que notre méthode vérifie correctement ses arguments.

Examinez à nouveau la méthode testée (BankAccount.Debit) et notez que les deux instructions conditionnelles utilisent un constructeur ArgumentOutOfRangeException qui prend simplement le nom de l’argument en tant que paramètre :

throw new ArgumentOutOfRangeException("amount");

Vous pouvez utiliser un constructeur qui signale des informations beaucoup plus riches : ArgumentOutOfRangeException(String, Object, String) inclut le nom de l’argument, la valeur de l’argument et un message défini par l’utilisateur. Vous pouvez refactoriser la méthode testée pour utiliser ce constructeur. Encore mieux, vous pouvez utiliser les membres de type disponibles publiquement pour spécifier les erreurs.

Refactoriser le code sous test

Tout d’abord, définissez deux constantes pour les messages d’erreur au niveau de l’étendue de la classe. Placez les définitions dans la classe sous test, BankAccount:

public const string DebitAmountExceedsBalanceMessage = "Debit amount exceeds balance";
public const string DebitAmountLessThanZeroMessage = "Debit amount is less than zero";

Ensuite, modifiez les deux instructions conditionnelles dans la méthode Debit :

if (amount > m_balance)
{
    throw new System.ArgumentOutOfRangeException("amount", amount, DebitAmountExceedsBalanceMessage);
}

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

Refactoriser les méthodes de test

Refactorisez les méthodes de test en supprimant l’appel à Assert.ThrowsException. Placez l’appel à Debit() dans un bloc try/catch, interceptez l’exception spécifique qui est attendue et vérifiez le message associé. La méthode Microsoft.VisualStudio.TestTools.UnitTesting.StringAssert.Contains permet de comparer deux chaînes.

À présent, le Debit_WhenAmountIsMoreThanBalance_ShouldThrowArgumentOutOfRange peut ressembler à ceci :

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

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

Retester, réécrire et réanalyser

Actuellement, la méthode de test ne gère pas tous les cas qu'elle devrait. Si la méthode testée, la méthode Debit, n’a pas réussi à lever un ArgumentOutOfRangeException lorsque le debitAmount était supérieur à l’équilibre (ou inférieur à zéro), la méthode de test aurait réussi. Ce scénario n’est pas correct, car vous souhaitez que la méthode de test échoue si aucune exception n’est levée.

Ce résultat est un bogue dans la méthode de test. Pour résoudre le problème, ajoutez une assertion Assert.Fail à la fin de la méthode de test pour gérer le cas où aucune exception n’est levée.

La réexécution du test montre que le test échoue maintenant si l’exception correcte est interceptée. Le bloc catch intercepte l’exception, mais la méthode continue à s’exécuter et échoue à la nouvelle Assert.Fail assert. Pour résoudre ce problème, ajoutez une instruction return après le StringAssert dans le bloc catch. La réexécution du test confirme que vous avez résolu ce problème. La version finale du Debit_WhenAmountIsMoreThanBalance_ShouldThrowArgumentOutOfRange ressemble à ceci :

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

    // Act
    try
    {
        account.Debit(debitAmount);
    }
    catch (System.ArgumentOutOfRangeException e)
    {
        // Assert
        StringAssert.Contains(e.Message, BankAccount.DebitAmountExceedsBalanceMessage);
        return;
    }

    Assert.Fail("The expected exception was not thrown.");
}

Conclusion

Les améliorations apportées au code de test ont conduit à des méthodes de test plus robustes et plus informatives. Mais plus important encore, ils ont également amélioré le code en cours de test.

Conseil

Cette procédure pas à pas utilise l’infrastructure de test unitaire Microsoft pour le code managé. L’Explorateur de tests peut aussi exécuter des tests à partir de frameworks de tests unitaires tiers qui ont des adaptateurs pour l’Explorateur de tests. Pour plus d’informations, consultez Installer des frameworks de test unitaire tiers.

Pour plus d’informations sur l’exécution de tests à partir d’une ligne de commande, consultez VSTest.Console.exe options de ligne de commande.