Skip to main content

Iniziare a utilizzare i test di unità Apex

Obiettivi di apprendimento

Al completamento di questa unità, sarai in grado di:

  • Descrivere i vantaggi principali dei test di unità Apex.
  • Definire una classe con metodi di test.
  • Eseguire tutti i metodi di test di una classe ispezionare gli errori.
  • Creare ed eseguire una serie di classi di test.
Nota

Nota

Stai seguendo la formazione in italiano? Inizia la sfida in un Trailhead Playground in italiano e utilizza le traduzioni fornite tra parentesi per la navigazione. Per quanto riguarda i valori da inserire, copia e incolla solo quelli in lingua inglese, perché la convalida della sfida è basata sul fatto che i dati siano in inglese. Se non superi la sfida nella tua organizzazione italiana, ti consigliamo di (1) selezionare Stati Uniti per le impostazioni internazionali, (2) selezionare Inglese per la lingua seguendo le istruzioni riportate qui e, successivamente, (3) fare nuovamente clic sul pulsante "Controlla la sfida".

Visita il badge Trailhead nella tua lingua per informazioni dettagliate su come usufruire dell'esperienza Trailhead in altre lingue.

Test di unità Apex

Il framework di testing Apex permette di scrivere ed eseguire test per le classi e i trigger Apex su Lightning Platform. I test di unità Apex garantiscono un'elevata qualità del codice Apex e permettono di soddisfare i requisiti per la distribuzione Apex.

I test sono fondamentali per il successo dello sviluppo a lungo termine e sono una componente cruciale del processo di sviluppo. Il framework di testing Apex semplifica il test del codice Apex. Il codice Apex può essere scritto solo in un ambiente sandbox o in un'organizzazione Developer, non in produzione. Il codice Apex può essere distribuito in un'organizzazione di produzione a partire da una sandbox. Inoltre, gli sviluppatori di app possono distribuire il codice Apex ai clienti delle rispettive organizzazioni Developer caricando i pacchetti su AppExchange per Lightning Platform. Oltre a essere fondamentali per garantire la qualità, i test di unità Apex costituiscono anche un requisito per la distribuzione e l'implementazione di Apex. 

I vantaggi dei test di unità Apex sono i seguenti.

  • Assicurare il corretto funzionamento delle classi e dei trigger Apex.
  • Disporre di una suite di test di regressione che è possibile rieseguire tutte le volte che le classi e i trigger vengono aggiornati, al fine di garantire che i futuri aggiornamenti dell'app non compromettano le funzionalità esistenti.
  • Soddisfare i requisiti di copertura del codice per la distribuzione di Apex in produzione o per la distribuzione di Apex ai clienti tramite pacchetti.
  • Fornire app di alta qualità all'organizzazione di produzione, rendendone più produttivi gli utenti.
  • Fornire app di alta qualità agli abbonati ai pacchetti, aumentando la fiducia dei clienti.
Nota

Prima di qualunque aggiornamento significativo del servizio, Salesforce esegue tutti i test Apex per conto tuo attraverso un processo chiamato Apex Hammer. Il processo Hammer viene eseguito nella versione corrente e in quella successiva e confronta i risultati dei test. Questo processo controlla che gli aggiornamenti del servizio non abbiano modificato il comportamento del tuo codice personalizzato. Il processo Hammer sceglie le organizzazioni in modo selettivo e non viene eseguito in tutte le organizzazioni. I problemi rilevati vengono suddivisi per priorità in base a determinati criteri. Salesforce cerca di risolvere tutti i problemi riscontrati prima di ogni nuova versione.

La nostra priorità principale è garantire la sicurezza dei dati. Non visualizziamo e non modifichiamo alcun dato nell'organizzazione e tutti i test sono condotti in una copia che viene eseguita in un centro dati protetto.

Requisiti di copertura del codice per la distribuzione

Prima di poter distribuire il codice o di inviarlo ad AppExchange in Lightning Platform, almeno il 75% del codice Apex deve essere stato sottoposto a test e tutti i test devono essere stati superati. Oltre a ciò, ciascun trigger deve avere una certa copertura. Anche se la copertura del codice è un requisito per la distribuzione, non scrivere test solo per soddisfarlo. Assicurati di effettuare i test sui casi d'uso più comuni dell'app, ivi compresi i casi di test positivi e negativi e l'elaborazione di record singoli e in blocco.

Sintassi dei metodi di test

I metodi di test vengono definiti utilizzando l'annotazione @isTest e presentano la seguente sintassi:

@isTest static void testName() {

  // code_block

}

L'annotazione @isTest accetta più modificatori tra parentesi e separati da spazi vuoti. Tratteremo uno di questi parametri più avanti.

La visibilità di un metodo di test non ha importanza, quindi dichiarare un metodo di test come pubblico o privato non fa differenza, poiché il framework di testing è sempre in grado di accedere ai metodi di test. Per questo motivo, i modificatori di accesso sono omessi nella sintassi.

I metodi di test devono essere definiti in classi di test, che sono classi annotate tramite @isTest. Questa classe di esempio mostra la definizione di una classe di test con un metodo di test.

@isTest

private class MyTestClass {

  @isTest static void myTest() {

    // code_block

  }

}

Le classi di test possono essere private o pubbliche. Se si utilizza una classe di test solo per i test di unità, è necessario dichiararla come privata. Le classi di test pubbliche sono normalmente utilizzate per le classi data factory di test, che verranno trattate più avanti.

Esempio di test di unità: test della classe TemperatureConverter

Nel semplice esempio riportato di seguito è definita una classe di test con tre metodi di test. Il metodo di classe che si sta sottoponendo al test prende come input una temperatura in gradi Fahrenheit. Converte quella temperatura in gradi Celsius e restituisce il risultato convertito. Aggiungiamo la classe personalizzata e la relativa classe di test.

  1. Nella Developer Console, fai clic su File | New (Nuovo) | Apex Class (Classe Apex) e inserisci TemperatureConverter come nome della classe, quindi fai clic su OK.
  2. Sostituisci il corpo predefinito della classe con il codice seguente.
    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. Premi Ctrl+S per salvare la classe.
  4. Ripeti i passaggi precedenti per creare la classe TemperatureConverterTest. Aggiungi il codice seguente a questa 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 di test TemperatureConverterTest verifica che il metodo funzioni come previsto chiamandolo con diversi input per la temperatura in gradi Fahrenheit. Ciascun metodo di test verifica un tipo di input: una temperatura calda, una temperatura di congelamento, una temperatura di ebollizione e una temperatura negativa. Le verifiche vengono effettuate chiamando il metodo System.assertEquals(), che utilizza due parametri: il primo è il valore atteso e il secondo è il valore effettivo. Esiste un'altra versione di questo metodo che accetta un terzo parametro, una stringa che descrive il confronto in corso, utilizzata in testBoilingPoint(). Questa stringa opzionale viene registrata se l'asserzione non riesce.

Eseguiamo i metodi di questa classe.

  1. Nella Developer Console, fai clic su Test | New Run (Nuova esecuzione).
  2. In Test Classes (Classi di test), fai clic su TemperatureConverterTest.
  3. Per aggiungere all'esecuzione del test tutti i metodi di test della classe TemperatureConverterTest, fai clic su Add Selected (Aggiungi selezionate).
  4. Fai clic su Run (Esegui).
  5. Nella scheda Tests (Test) è possibile vedere lo stato dei test in fase di esecuzione. Espandi l'esecuzione del test ed espandi ancora fino a visualizzare l'elenco dei singoli test eseguiti. Hanno tutti un segno di spunta verde. 
    Controlla i risultati dei test nella Developer Console

Dopo aver eseguito i test, la copertura del codice viene generata automaticamente per le classi Apex e i trigger dell'organizzazione. Puoi controllare la percentuale di copertura del codice nella scheda Tests (Test) della Developer Console. In questo esempio, la classe che è stata testata, la classe TemperatureConverter, ha una copertura del 100%, come mostrato in questa immagine.

Visualizzazione della percentuale di copertura del codice nella Developer Console

Nota

Ogni volta che si modifica il codice Apex, è necessario rieseguire i test per aggiornare i risultati della copertura del codice.

Un problema noto della Developer Console impedisce di aggiornare correttamente la copertura del codice quando si esegue un sottoinsieme di test. Per aggiornare i risultati della copertura del codice, bisogna utilizzare Test | Run All (Esegui tutto) anziché Test | New Run (Nuova esecuzione).

Anche se è vero che un solo metodo di test avrebbe portato alla copertura completa della classe TemperatureConverter, è comunque importante effettuare test su diversi input per garantire la qualità del codice. Chiaramente non è possibile verificare ciascun punto dati, ma è possibile verificare punti dati comuni e diversi intervalli di input. Ad esempio, è possibile verificare il passaggio di numeri positivi e negativi, di valori limite e di valori di parametri non validi per verificare il comportamento negativo. I test per la classe TemperatureConverter verificano i punti dati comuni, come la temperatura di ebollizione e le temperature negative.

La classe di test TemperatureConverterTest non copre gli input non validi o le condizioni limite. Le condizioni limite si riferiscono ai valori minimi e massimi. In questo caso, il metodo di conversione della temperatura accetta un valore di tipo Decimal, che può contenere numeri grandi, superiori ai valori di tipo Double. Per quanto riguarda gli input non validi, non esiste una temperatura non valida, ma l'unico input non valido è l'input nullo. Come viene gestito questo valore dal metodo di conversione? In questo caso, quando il runtime Apex dereferenzia la variabile parametro per valutare la formula, genera una System.NullPointerException. È possibile modificare il metodo FahrenheitToCelsius() in modo che verifichi la presenza di un input non valido e, in tal caso, restituisca null, quindi aggiungere un test per verificare il comportamento dell'input non valido.

Fino a questo punto, tutti i test vengono superati perché la formula di conversione utilizzata nel metodo di classe è corretta. Che noia, però! Proviamo a simulare un errore per vedere cosa succede quando un'asserzione non riesce. Ad esempio, modifichiamo il test relativo alla temperatura di ebollizione e inseriamo un valore atteso falso per la temperatura di ebollizione in gradi Celsius (0 anziché 100). Questo comporta un errore nel metodo di test corrispondente.

  1. Modifica il metodo di test testBoilingPoint() come mostrato di seguito.
    @isTest
    static void testBoilingPoint() {
      Decimal celsius = TemperatureConverter.FahrenheitToCelsius(212);
      // Simulate failure
      System.assertEquals(0,celsius,'Boiling point temperature is not expected.');
    }
  2. Per eseguire lo stesso test, fai clic sull'ultima esecuzione nella scheda Tests (Test), quindi fai clic su Test | Rerun (Esegui di nuovo). L'asserzione in testBoilingPoint() non riesce e genera un errore irreversibile (un'eccezione AssertException che non può essere intercettata).
  3. Controlla i risultati nella scheda Tests (Test) espandendo l'ultimo test eseguito. L'esecuzione del test riporta che un test su quattro non è riuscito. Per avere maggiori dettagli sull'errore, fai doppio clic sull'esecuzione del test. I risultati dettagliati sono visualizzati in una scheda separata, come mostrato in questa immagine. 
    [Alt text: Controllo dei risultati di un test non riuscito nella Developer Console]
  4. Per visualizzare il messaggio di errore del test non riuscito, fai doppio clic all'interno della colonna Errors (Errori) del test non riuscito. Vedrai il messaggio seguente. Il testo descrittivo accanto ad Assertion Failed: (Asserzione non riuscita:) è il testo che era stato fornito nell'istruzione System.assertEquals().

    System.AssertException:Assertion Failed:Boiling point temperature is not expected.:Expected:0, Actual:100.00 (Asserzione non riuscita: temperatura del punto di ebollizione inattesa. Attesa: 0, Effettiva: 100,00)

I dati di questi metodi di test sono numeri e non record Salesforce. Nella prossima unità, scoprirai come effettuare test sui record Salesforce e come impostare i dati.

Aumentare la copertura del codice

Nello scrivere i test, cerca di raggiungere la massima copertura del codice possibile. Non limitarti a puntare al 75% di copertura, che è la copertura minima richiesta da Lightning Platform per le implementazioni e i pacchetti. Più sono i casi di test coperti dai tuoi test, maggiori sono le probabilità che il tuo codice sia solido. A volte, anche dopo aver scritto metodi di test per tutti i metodi di classe, la copertura del codice non raggiunge il 100%. Uno dei motivi più comuni è la mancata copertura di tutti i valori dei dati per l'esecuzione di codice condizionale. Ad esempio, i valori di alcuni dati tendono a essere ignorati quando il metodo di classe contiene istruzioni if che causano l'esecuzione di rami diversi in base al fatto che la condizione valutata sia soddisfatta o meno. Assicurati che i metodi di test tengano conto di questi diversi valori.

Questo esempio include il metodo di classe getTaskPriority(), che contiene due istruzioni if. Il compito principale di questo metodo è quello di restituire una stringa che esprime una priorità basata sullo stato di residenza del lead indicato. Il metodo dapprima convalida lo stato e poi restituisce null se lo stato non è valido. Se lo stato è uguale a CA (California), il metodo restituisce "High" (Alta). In caso contrario, restituisce "Normal" (Normale) per qualsiasi altro valore dello stato.

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;

  }

}
Nota

L'operatore di uguaglianza (==) esegue confronti tra stringhe senza differenziare tra maiuscole e minuscole. Quindi non è necessario convertire prima i caratteri della stringa in caratteri minuscoli. Ciò significa che se si inserisce "ca" o "Ca" si soddisfa la condizione di uguaglianza con il valore letterale "CA" della stringa.

Questa è la classe di test per il metodo getTaskPriority(). Il metodo di test chiama semplicemente getTaskPriority() con uno stato ("NY").

@isTest

private class TaskUtilTest {

  @isTest
  static void testTaskPriority() {

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

    System.assertEquals('Normal', pri);

  }

}

Eseguiamo questa classe di test (TaskUtilTest) nella Developer Console e verifichiamo la copertura del codice per la classe TaskUtil corrispondente coperta da questo test. Al termine dell'esecuzione del test, la copertura del codice per TaskUtil viene indicata al 75%. Aprendo questa classe nella Developer Console, si vedono sei righe blu (coperte) e due righe rosse (non coperte), come mostrato in questa immagine.

Righe coperte per la classe TaskUtil nella Developer Console

Il motivo per cui la riga 5 non è stata coperta è che la nostra classe di test non conteneva un test per inserire un parametro di stato non valido. Allo stesso modo, la riga 11 non è stata coperta perché il metodo di test non ha inserito "CA" come stato. Aggiungiamo altri due metodi di test per coprire questi scenari. Di seguito viene mostrata la classe di test completa dopo l'aggiunta dei metodi di test testTaskHighPriority() e testTaskPriorityInvalid(). Se esegui nuovamente questa classe di test utilizzando i comandi Run all (Esegui tutto) o New Run (Nuova esecuzione), la copertura del codice per TaskUtil è ora al 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);

  }

}

Creare ed eseguire una suite di test

Una suite di test è una raccolta di classi di test Apex che vengono eseguite insieme. Ad esempio, è possibile creare una suite di test da eseguire ogni volta che si prepara una distribuzione o che Salesforce rilascia una nuova versione. Puoi anche configurare una suite di test nella Developer Console per definire un insieme di classi di test da eseguire regolarmente insieme.

In questo momento hai due classi di test nell'organizzazione. Queste due classi non sono correlate, ma per il momento facciamo finta che lo siano. Supponiamo che vi siano situazioni in cui vuoi eseguire queste due classi di test ma non vuoi eseguire tutti i test presenti nell'organizzazione. Puoi creare una suite di test che contenga entrambe le classi ed eseguire i test della suite.

  1. Nella Developer Console, seleziona Test | New Suite (Nuova suite).
  2. Inserisci TempConverterTaskUtilSuite come nome della suite, quindi fai clic su OK.
  3. Seleziona TaskUtilTest, tieni premuto il tasto Ctrl, quindi seleziona TemperatureConverterTest.
  4. Per aggiungere alla suite le classi di test selezionate, fai clic su >Finestra di modifica della suite di test con due classi di test selezionate
  5. Fai clic su Save (Salva).
  6. Seleziona Test | New Suite Run (Esecuzione nuova suite).
  7. Seleziona TempConverterTaskUtilSuite, quindi fai clic su > per spostare TempConverterTaskUtilSuite nella colonna Selected Test Suites (Suite di test selezionate).
  8. Fai clic su Run Suites (Esegui suite).
  9. Nella scheda Tests (Test) è possibile monitorare lo stato dei test in fase di esecuzione. Espandi l'esecuzione del test ed espandi ancora fino a visualizzare l'elenco dei singoli test eseguiti. Come durante l'esecuzione di singoli metodi di test, è possibile fare doppio clic sui nomi dei metodi per visualizzare i risultati dettagliati dei test.

Creare i dati di test

I record Salesforce creati nei metodi di test non vengono salvati nel database. Al termine dell'esecuzione del test, sono soggetti a un rollback. Il rollback è utile per effettuare i test perché evita la necessità di ripulire i dati del test dopo la sua esecuzione.

Per impostazione predefinita, i test Apex non hanno accesso ai dati preesistenti dell'organizzazione, ad eccezione dell'accesso agli oggetti relativi alle impostazioni e ai metadati, ad esempio gli oggetti User (Utente) o Profile (Profilo). Imposta i dati per i test. La creazione di dati di test rende i test più solidi e previene errori dovuti alla mancanza o alla modifica dei dati nell'organizzazione. È possibile creare i dati di test direttamente nel metodo di test o utilizzando una classe di test di utilità, come vedremo più avanti.

Nota

Anche se non è una best practice, ci sono casi in cui un metodo di test deve poter accedere a dati preesistenti. Per accedere ai dati dell'organizzazione, è necessario annotare il metodo di test con @isTest(SeeAllData=true). Gli esempi di metodi di test presentati in questa unità non accedono ai dati dell'organizzazione e quindi non utilizzano il parametro SeeAllData.

Ulteriori informazioni

  • Per eseguire test con Apex e verificare la funzionalità del tuo codice, puoi utilizzare l'estensione Apex Salesforce per Visual Studio Code.
  • È possibile salvare fino a 6 MB di codice Apex in ciascuna organizzazione. Le classi di test annotate con @isTest non contano ai fini di questo limite.
  • Anche se viene eseguito il rollback dei dati di test, non viene utilizzato un database separato per i test. Di conseguenza, per alcuni sObject che hanno campi con vincoli univoci, l'inserimento di record sObject duplicati genera un errore.
  • I metodi di test non inviano email.
  • I metodi di test non possono effettuare callout verso servizi esterni. Durante i test è possibile utilizzare callout simulati.
  • Le ricerche SOSL eseguite in un test restituiscono risultati vuoti. Se si vogliono ottenere risultati prevedibili, è necessario utilizzare Test.setFixedSearchResults() per definire i record che verranno restituiti dalla ricerca.

Risorse

Prepararsi per la sfida pratica

Per completare la sfida pratica per questa unità dovrai creare una nuova classe denominata VerifyDate copiando il codice riportato di seguito:

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;

  }

}
Condividi il tuo feedback su Trailhead dalla Guida di Salesforce.

Conoscere la tua esperienza su Trailhead è importante per noi. Ora puoi accedere al modulo per l'invio di feedback in qualsiasi momento dal sito della Guida di Salesforce.

Scopri di più Continua a condividere il tuo feedback