Skip to main content

Premiers pas avec les tests unitaires Apex

Objectifs de formation

Une fois cette unité terminée, vous pourrez :

  • Présenter les principaux avantages des tests unitaires Apex
  • Définir une classe avec des méthodes de test
  • Exécuter toutes les méthodes de test dans une classe et inspecter les échecs
  • Créer et exécuter une suite de classes de test
Remarque

Remarque

Vous souhaitez apprendre en français ? Commencez le défi dans un Trailhead Playground en français et utilisez les traductions fournies entre crochets pour naviguer. Copiez et collez uniquement les valeurs en anglais, car les validations de défi reposent sur les données en anglais. Si vous ne réussissez pas le défi dans votre organisation en français, nous vous recommandons (1) de définir le paramètre régional sur les États-Unis, (2) de définir la langue sur l’anglais en suivant les instructions ici, puis (3) de cliquer à nouveau sur le bouton « Vérifier le défi ».

Consultez le badge Trailhead dans votre langue pour découvrir comment profiter de l’expérience Trailhead traduite.

Tests unitaires Apex

L'infrastructure de test Apex permet d'écrire et d'exécuter des tests pour vos classes et déclencheurs Apex sur Lightning Platform. Les tests unitaires Apex assurent une qualité élevée pour votre code Apex et vous permettent de satisfaire aux exigences de déploiement Apex.

La réalisation de tests est la clé de la réussite du développement à long terme et constitue un aspect essentiel du processus de développement. L'infrastructure de test Apex facilite le test de votre code Apex. Un code Apex peut être écrit uniquement dans un environnement sandbox ou une organisation Developer, pas dans une organisation de production. Un code Apex peut être déployé vers une organisation de production depuis une sandbox. Les développeurs d’applications peuvent également distribuer un code Apex aux clients depuis leur organisation Developer en chargeant des packages sur AppExchange de​la plate-forme Lightning. Outre leur importance pour le contrôle qualité, les tests unitaires Apex sont également requis pour le déploiement et la distribution d'un code Apex. 

Les tests unitaires Apex offrent les avantages suivants :

  • Ils garantissent le fonctionnement normal de vos classes et déclencheurs Apex
  • Ils fournissent une suite de tests de régression qui peuvent être réexécutés à chaque mise à jour de classes ou de déclencheurs afin de s'assurer que les mises à jour ultérieures de votre application ne détériorent pas les fonctionnalités actuelles
  • Ils satisfont aux exigences de couverture de code du déploiement d'Apex vers une organisation de production ou de distribution de code Apex aux clients via des packages
  • Ils fournissent à l'organisation de production des applications de grande qualité, qui augmentent la productivité des utilisateurs
  • Ils fournissent aux abonnés de packages des applications de grande qualité, qui augmentent la confiance de vos clients
Remarque

Avant chaque mise à niveau de service majeure, Salesforce exécute tous les tests Apex en votre nom via un processus appelé Apex Hammer. Le processus Hammer est exécuté dans la version actuelle et dans la nouvelle version, et compare les résultats des tests. Ce processus vérifie que le comportement de votre code personnalisé n’est pas altéré par les mises à niveau du service. Le processus Hammer choisit les organisations de façon sélective. Il n’est pas exécuté dans toutes les organisations. Les problèmes détectés sont triés selon des critères précis. Salesforce s’efforce de corriger tous les problèmes détectés avant toute nouvelle publication.

Notre priorité est le maintien du niveau de sécurité de vos données. Nous ne visualisons ni ne modifions aucune donnée de votre organisation, et tous les tests sont exécutés sur une copie exécutée dans un centre de données sécurisé.

Configuration de couverture de code requise pour un déploiement

Afin de pouvoir déployer votre code ou l’empaqueter pour AppExchange​de la plate-forme Lightning, au moins 75 % du code Apex doit être testé, et ce, avec succès. En outre, chaque déclencheur doit également avoir une couverture. Bien que la couverture de code soit une exigence pour le déploiement, vous ne devez pas écrire de tests uniquement dans le but de vous y conformer. Veillez bien à tester les cas d'utilisation communs dans votre application, y compris les cas de tests positifs et négatifs et le traitement en masse ou unique des enregistrements.

Syntaxe d'une méthode de test

Les méthodes de test sont définies à l’aide de l’annotation @isTest et ont la syntaxe suivante :

@isTest static void testName() {

  // code_block

}

L’annotation @isTest possède plusieurs modificateurs entre parenthèses et séparés par des espaces. Nous évoquerons ce paramètre ultérieurement.

La visibilité d'une méthode de test n'a aucune importance. Par conséquent, le fait de déclarer une méthode de test comme publique ou privée est sans incidence, car l'infrastructure de test peut toujours accéder aux méthodes de test. Pour cette raison, les modificateurs d'accès ne sont pas inclus dans la syntaxe.

Les méthodes de test doivent être définies dans des classes de test, qui sont annotées avec @isTest. Cet exemple de classe présente une définition de classe de test avec une méthode de test.

@isTest

private class MyTestClass {

  @isTest static void myTest() {

    // code_block

  }

}

Les classes de test peuvent être privées ou publiques. Si vous utilisez une classe de test uniquement pour un test unitaire, déclarez-la comme privée. Les classes de test publiques sont généralement utilisées pour des classes de génération de données test, que nous présenterons ultérieurement.

Exemple de test unitaire : test de la classe TemperatureConverter

L'exemple simple ci-dessous présente une classe de test avec trois méthodes de test. La méthode de classe qui est testée accepte une entrée de température en Fahrenheit. Elle convertit cette température en Celsius et renvoie le résultat converti. Ajoutons la classe personnalisée et sa classe de test.

  1. Dans la Developer Console, cliquez sur File (Fichier) | New (Nouveau) | Apex Class (Classe Apex), saisissez TemperatureConverter comme nom de classe, puis cliquez sur OK (OK).
  2. Remplacez le corps de classe par défaut par ce qui suit :
    public class TemperatureConverter {
      // Takes a Fahrenheit temperature and returns the Celsius equivalent.
      public static Decimal FahrenheitToCelsius(Decimal fh) {
        Decimal cs = (fh - 32) * 5/9;
        return cs.setScale(2);
      }
    }
  3. Appuyez sur Ctrl + S pour enregistrer votre classe.
  4. Répétez les étapes précédentes pour créer la classe TemperatureConverterTest. Ajoutez l'élément suivant pour cette classe :
    @isTest
    private class TemperatureConverterTest {
      @isTest static void testWarmTemp() {
        Decimal celsius = TemperatureConverter.FahrenheitToCelsius(70);
        System.assertEquals(21.11,celsius);
      }
      @isTest static void testFreezingPoint() {
        Decimal celsius = TemperatureConverter.FahrenheitToCelsius(32);
        System.assertEquals(0,celsius);
      }
      @isTest static void testBoilingPoint() {
        Decimal celsius = TemperatureConverter.FahrenheitToCelsius(212);
        System.assertEquals(100,celsius,'Boiling point temperature is not expected.');
      }
      @isTest static void testNegativeTemp() {
        Decimal celsius = TemperatureConverter.FahrenheitToCelsius(-10);
        System.assertEquals(-23.33,celsius);
      }
    }

La classe de test TemperatureConverterTest vérifie que la méthode fonctionne normalement en l'appelant avec différentes entrées de température en Fahrenheit. Chaque méthode de test vérifie un type d'entrée : une température chaude, la température du point de congélation, la température du point d'ébullition et une température négative. Les vérifications sont effectuées en appelant la méthode System.assertEquals() qui utilise deux paramètres : le premier est la valeur attendue et le deuxième est la valeur réelle. Une autre version de cette méthode existe. Elle utilise un troisième paramètre, une chaîne qui décrit la comparaison effectuée et utilisée dans testBoilingPoint(). Cette chaîne facultative est consignée si l'assertion échoue.

Exécutons les méthodes de cette classe.

  1. Dans la Developer Console, cliquez sur Test | New Run (Nouvelle exécution).
  2. Sous Test Classes (Classes de test), cliquez sur TemperatureConverterTest.
  3. Pour ajouter toutes les méthodes de test de la classe TemperatureConverterTest à l’exécution du test, cliquez sur Add Selected (Ajouter sélection).
  4. Cliquez sur Run (Exécuter).
  5. Le statut de vos tests en cours d'exécution est affiché sous l'onglet Tests. Développez l'exécution du test jusqu'à ce que la liste des tests individuels exécutés s'affiche. Une coche verte est affichée en regard de chaque test. 
    Inspection des résultats de test dans la Developer Console

Après avoir exécuté des tests, la couverture de code est automatiquement générée pour les classes et déclencheurs Apex dans l'organisation. Vous pouvez contrôler le pourcentage de couverture de code sous l'onglet Tests de la Developer Console. Dans cet exemple, la classe que vous avez testée, TemperatureConverter, a 100 % de couverture comme illustré ici.

Affichage du pourcentage de couverture de code dans la Developer Console

Remarque

Chaque fois que vous modifiez votre code Apex, réexécutez vos tests afin d'actualiser les résultats de couverture de code.

Un problème connu dans Developer Console empêche la mise à jour de la couverture de code lors de l'exécution d'un sous-ensemble de tests. Pour mettre à jour vos résultats de couverture de code, utilisez Test (Tester) | Run All (Tout exécuter) plutôt que Test (Tester) | New Run (Nouvelle exécution).

Une seule méthode de test suffit pour renvoyer une couverture complète de la classe TemperatureConverter, mais il est important de tester différentes entrées pour vérifier la qualité de votre code. Il n'est pas possible de vérifier chaque point de données. Vous pouvez toutefois tester les points de données courants et différentes plages d'entrées. Vérifiez par exemple la transmission de nombres positifs et négatifs, de valeurs de limitations, ainsi que les valeurs de paramètres non valides pour vérifier un comportement négatif. Les tests de la classe TemperatureConverter vérifient des points de données courants, par exemple la température d'ébullition et des températures négatives.

La classe de test TemperatureConverterTest ne couvre pas les entrées non valides ni les conditions de limitation. Les conditions de limitation sont proches des valeurs minimales et maximales. Dans le cas présent, la méthode de conversion de la température accepte une valeur de type Decimal (décimale), qui peut accepter de grands nombres supérieurs aux valeurs Double (double). Aucune température non valide n’existe pour les entrées non valides, la seule entrée correspondante est null. Comment la méthode de conversion gère-t-elle cette valeur ? Dans le cas présent, lorsque l'exécution de code Apex annule la référence de la variable de paramètre pour évaluer la formule, elle renvoie une System.NullPointerException. Vous pouvez modifier la méthode FahrenheitToCelsius() afin de vérifier la présence d’une entrée non valide et de renvoyer null, puis ajouter un test pour vérifier le comportement de l’entrée non valide.

Jusqu'à présent, tous les tests réussissent, car la formule de conversion utilisée dans la méthode de classe est correcte. Mais ce n'est pas intéressant ! Essayons de simuler un échec pour observer ce qui se passe lorsqu'une assertion échoue. Par exemple, modifions le test de température du point d'ébullition Celsius et transmettons une fausse valeur attendue (0 au lieu de 100). La méthode de test correspondante échoue.

  1. Modifiez la méthode de test testBoilingPoint() comme suit :
    @isTest
    static void testBoilingPoint() {
      Decimal celsius = TemperatureConverter.FahrenheitToCelsius(212);
      // Simulate failure
      System.assertEquals(0,celsius,'Boiling point temperature is not expected.');
    }
  2. Pour exécuter la même exécution de test, cliquez sur la dernière exécution dans l’onglet Tests (Tests), puis sur Test (Tester) | Rerun (Réexécuter). L’assertion dans testBoilingPoint() échoue et renvoie une erreur fatale (une AssertException impossible à intercepter).
  3. Vérifiez les résultats sous l'onglet Tests en développant la dernière exécution de test. L'exécution de test indique qu'un test sur quatre a échoué. Pour plus d'informations sur l'échec, double-cliquez sur l'exécution du test. Des résultats détaillés s'affichent sous un autre onglet, comme illustré dans cette image. 
    [Texte Alt : Examen des résultats d’un test échoué dans la Developer Console]
  4. Pour afficher le message d'erreur de l'échec du test, double-cliquez dans la colonne Errors (Erreurs) du test échoué. Vous verrez ce qui suit : le texte descriptif en regard de Assertion Failed: (Échec de l’assertion :) est le texte que nous avons fourni dans l’instruction System.assertEquals() .

    System.AssertException:Assertion Failed:Boiling point temperature is not expected.:Expected:0, Actual:100.00 (System.AssertException:Échec de l’assertion : la température du point d’ébullition n’est pas celle attendue.:Attendue : 0, Réelle : 100,00)

Les données test de ces méthodes de test sont des chiffres, pas des enregistrements Salesforce. L'unité suivante présente des informations supplémentaires sur le test des enregistrements Salesforce et la configuration de vos données.

Augmentation de votre couverture de code

Lors de l'écriture de tests, essayez d'obtenir la meilleure couverture de code possible. Ne cherchez pas à couvrir seulement 75 % du code. Il s’agit de la couverture minimale exigée par la plate-forme Lightning pour des déploiements et des packages. Plus vos tests couvrent de situations, plus vous augmentez la probabilité que votre code soit robuste. Parfois, même après avoir écrit des méthodes de test pour toutes vos méthodes de classe, la couverture de code n'atteint pas 100 %. Cela provient généralement d'une couverture incomplète de toutes les valeurs des données d'une exécution de code conditionnelle. Par exemple, certaines valeurs de données sont souvent ignorées lorsque votre méthode de classe contient des instructions if qui entraînent l’exécution de différentes branches selon que la condition évaluée est satisfaite ou non. Assurez-vous que vos méthodes de test prennent en compte les différentes valeurs.

Cet exemple inclut la méthode de classe, getTaskPriority(), qui contient deux instructions if. La principale tâche de cette méthode est de renvoyer une valeur de chaîne de priorité en fonction du statut de piste donné. La méthode valide d’abord le statut et renvoie null si le statut n’est pas valide. Si le statut est CA, la méthode renvoie « High », sinon elle renvoie « Normal » pour les autres valeurs de statut.

public class TaskUtil {

  public static String getTaskPriority(String leadState) {

    // Validate input

    if(String.isBlank(leadState) || leadState.length() > 2) {

      return null;

    }

    String taskPriority;

    if(leadState == 'CA') {

      taskPriority = 'High'; 

    } else {

      taskPriority = 'Normal';

    }

    return taskPriority;

  }

}
Remarque

L'opérateur d'égalité (==) effectue des comparaisons de chaîne insensibles à la casse, il n'est pas nécessaire de convertir au préalable la chaîne en minuscules. Cela signifie que la transmission de 'ca' ou 'Ca' remplit la condition d’égalité avec la chaîne littérale 'CA'.

Il s'agit de la classe de test pour la méthode getTaskPriority(). La méthode de test appelle simplement getTaskPriority() avec un statut ('NY').

@isTest

private class TaskUtilTest {

  @isTest
  static void testTaskPriority() {

    String pri = TaskUtil.getTaskPriority('NY');

    System.assertEquals('Normal', pri);

  }

}

Exécutons cette classe de test (TaskUtilTest) dans la Developer Console, puis contrôlons la couverture de code de la classe correspondante TaskUtil qui est couverte par ce test. Une fois l'exécution du test terminée, il est indiqué que la couverture de code de TaskUtil est de 75 %. Si vous ouvrez cette classe dans la Developer Console, six lignes bleues (les lignes couvertes) et deux lignes rouges (les lignes non couvertes) sont affichées, comme illustré dans cette image.

Lignes couvertes de la classe TaskUtil dans la Developer Console

La ligne 5 n'est pas couverte, car notre classe de test ne contenait aucun test de transmission d'un paramètre de statut non valide. De la même façon, la ligne 11 n’est pas couverte, car la méthode de test n’a pas transmis de statut 'CA'. Ajoutons deux méthodes de test pour couvrir ces scénarios. L'image suivante affiche la classe de test complète après l'ajout des méthodes de test testTaskHighPriority() et testTaskPriorityInvalid(). Si vous réexécutez cette classe de test en utilisant Run All (Tout exécuter) ou New Run (Nouvelle exécution), il est indiqué que la couverture de code de TaskUtil est désormais de 100 % !

@isTest

private class TaskUtilTest {

  @isTest

  static void testTaskPriority() {

    String pri = TaskUtil.getTaskPriority('NY');

    System.assertEquals('Normal', pri);

  }

  @isTest

  static void testTaskHighPriority() {

    String pri = TaskUtil.getTaskPriority('CA');

    System.assertEquals('High', pri);

  }

  @isTest

  static void testTaskPriorityInvalid() {

    String pri = TaskUtil.getTaskPriority('Montana');

    System.assertEquals(null, pri);

  }

}

Création et exécution d'une suite de tests

Une suite de tests est un groupe de classes de test Apex que vous exécutez ensemble. Par exemple, vous pouvez créer une suite de tests que vous exécutez chaque fois que vous préparez un déploiement ou que Salesforce publie une nouvelle version. Configurez une suite de tests dans la Developer Console pour définir un groupe de classes de test que vous exécutez ensemble régulièrement.

Vous avez désormais deux classes de test dans votre organisation. Ces deux classes ne sont pas liées, mais supposons pour le moment qu'elles le sont. Imaginons que dans certaines circonstances, vous souhaitez exécuter ces deux classes de test, mais sans exécuter tous les tests de votre organisation. Créez une suite de tests contenant ces deux classes, puis exécutez les tests de cette suite.

  1. Dans la Developer Console, sélectionnez Test | New Suite (Nouvelle suite).
  2. Saisissez TempConverterTaskUtilSuite pour le nom de la suite, puis cliquez sur OK (OK).
  3. Sélectionnez TaskUtilTest, maintenez la touche Ctrl enfoncée, puis sélectionnez TemperatureConverterTest.
  4. Pour ajouter les classes de test sélectionnées à la suite, cliquez sur >Fenêtre de modification de la suite test avec deux classes de test sélectionnées
  5. Cliquez sur Save (Enregistrer).
  6. Sélectionnez Test | New Suite Run (Exécution de nouvelle suite).
  7. Sélectionnez TempConverterTaskUtilSuite, puis cliquez sur > pour placer TempConverterTaskUtilSuite dans la colonne Selected Test Suites (Suites de tests sélectionnées).
  8. Cliquez sur Run Suites (Exécuter les suites).
  9. Surveillez le statut de vos tests en cours d'exécution sous l'onglet Tests. Développez l'exécution du test jusqu'à ce que la liste des tests individuels exécutés s'affiche. Comme pour l'exécution de méthodes de test distinctes, vous pouvez double-cliquer sur des noms de méthodes afin d'afficher des résultats de tests détaillés.

Création de données test

Les enregistrements Salesforce créés dans des méthodes de tests ne sont pas enregistrés dans la base de données. Ils sont annulés à la fin de l'exécution du test. Ce comportement est pratique, il vous évite d'avoir à éliminer les données fictives après l'exécution de tests.

Par défaut, les tests Apex n'ont pas accès aux données existantes de l'organisation, sauf aux objets de configuration et de métadonnées, par exemple les objets User (Utilisateur) ou Profile (Profil). Configurez des données fictives pour vos tests. La création de données fictives rend les tests plus robustes et évite les échecs dus à des données manquantes ou modifiées dans l'organisation. Vous pouvez créer des données fictives directement dans votre méthode de test ou à l'aide d'une classe de test utilitaire, comme nous les verrons ultérieurement.

Remarque

Une méthode de test doit parfois accéder à des données existantes, même si ce n'est pas recommandé. Pour accéder aux données de l'organisation, annotez la méthode de test avec @isTest(SeeAllData=true). Les exemples de méthode de test de cette unité n'ont pas accès aux données de l'organisation et n'utilisent pas le paramètre SeeAllData.

En savoir plus

  • Vous pouvez utiliser l’extension Apex Salesforce pour Visual Studio Code afin d’exécuter des tests Apex et de vérifier la fonctionnalité de votre code.
  • Vous pouvez enregistrer jusqu’à 6 Mo de code Apex dans chaque organisation. Les classes de test annotées avec @isTest ne sont pas prises en compte dans cette limitation.
  • Bien que les données test soient éliminées, aucune base de données distincte n'est utilisée pour effectuer les tests. Ainsi, pour certains sObjects dont les champs doivent être uniques, l'insertion d'enregistrements sObjects dupliqués entraîne une erreur.
  • Les méthodes de test n'envoient aucun e-mail.
  • Les méthodes de test ne peuvent pas émettre des appels externes vers des services externes. Vous pouvez utiliser des appels externes fictifs dans des tests.
  • Les recherches SOSL effectuées dans un test renvoient des résultats vides. Pour garantir des résultats prévisibles, utilisez Test.setFixedSearchResults() afin de définir les enregistrements que la recherche doit renvoyer.

Ressources

Préparation au défi pratique

Afin de relever le défi pratique de cette unité, vous devrez créer une classe Apex nommée VerifyDate à l’aide du code que vous pouvez copier ci-dessous :

public class VerifyDate {

  //method to handle potential checks against two dates

  public static Date CheckDates(Date date1, Date date2) {

    //if date2 is within the next 30 days of date1, use date2.  Otherwise use the end of the month

    if(DateWithin30Days(date1,date2)) {

      return date2;

    } else {

      return SetEndOfMonthDate(date1);

    }

  }

  //method to check if date2 is within the next 30 days of date1

  private static Boolean DateWithin30Days(Date date1, Date date2) {

    //check for date2 being in the past

    if( date2 < date1) { return false; }

    //check that date2 is within (>=) 30 days of date1

    Date date30Days = date1.addDays(30); //create a date 30 days away from date1

    if( date2 >= date30Days ) { return false; }

    else { return true; }

  }

  //method to return the end of the month of a given date

  private static Date SetEndOfMonthDate(Date date1) {

    Integer totalDays = Date.daysInMonth(date1.year(), date1.month());

    Date lastDay = Date.newInstance(date1.year(), date1.month(), totalDays);

    return lastDay;

  }

}
Partagez vos commentaires sur Trailhead dans l'aide Salesforce.

Nous aimerions connaître votre expérience avec Trailhead. Vous pouvez désormais accéder au nouveau formulaire de commentaires à tout moment depuis le site d'aide Salesforce.

En savoir plus Continuer à partager vos commentaires