JUnit Test parametrizzato con esempio utilizzando @Parameters

โšก Riepilogo intelligente

Test parametrizzati in JUnit Eseguire ripetutamente lo stesso metodo di test con diversi valori di input, in modo che un singolo metodo copra molti scenari. Le annotazioni @RunWith e @Parameters forniscono il set di dati per ogni iterazione.

  • ๐Ÿ”˜ Fonte dati: Un metodo statico @Parameters restituisce una Collection di array, e ogni array diventa un'iterazione del test.
  • โ˜‘๏ธ Corridore: @RunWith(Parameterized.class) sostituisce il blocco predefinitoJUnit4ClassRunner e ricostruisce la classe una volta per ogni riga di dati.
  • โœ… Costruttore: Un costruttore pubblico memorizza una singola riga di dati nei campi dell'istanza su cui il metodo di test effettua l'asserzione.
  • ๐Ÿงช Esempio pratico: Cinque righe di input guidano il test sum() e il JUnit Visualizza i report. Eseguito 5/5 senza alcun errore.
  • ๏ธ JUnit 5: L'annotazione @ParameterizedTest con @ValueSource, @CsvSource o @MethodSource rimuove sia il runner che il costruttore.
  • ???? insidie: Un metodo @Parameters non statico, due costruttori pubblici o la mancanza della dipendenza junit-jupiter-params interrompono l'esecuzione.

JUnit Test parametrizzato tramite le annotazioni @RunWith e @Parameters

Che cos'รจ un test parametrizzato in JUnit?

A test parametrizzato Si tratta di un test che esegue ripetutamente lo stesso metodo di test utilizzando valori diversi. Aiuta gli sviluppatori a risparmiare tempo nella scrittura di test che differiscono solo per gli input e i risultati attesi.

Utilizzando un test parametrizzato, รจ possibile impostare un metodo di test che recupera i dati da una sorgente dati. Questo lo rende la forma piรน semplice di test guidato dai dati disponibile all'interno JUnit da solo, senza bisogno di librerie esterne.

Consideriamo un semplice test che somma diversi numeri. Il codice potrebbe essere simile a questo:

JUnit metodo di test che ripete tre chiamate assertEquals per il metodo sum

L'approccio sopra descritto genera molta ridondanza. Ogni nuova coppia di numeri richiede un'ulteriore istruzione assert all'interno dello stesso metodo, e un errore nella prima asserzione nasconde tutte le asserzioni successive.

Serve un approccio piรน semplice. Utilizzando un test parametrizzato, รจ possibile aggiungere un singolo metodo che fornisca dieci input di dati e il test verrร  eseguito automaticamente dieci volte.

Passaggi per creare un file con parametri JUnit Test

Il codice seguente mostra un esempio di test parametrizzato. Verifica il metodo sum() della classe Airthematic, che รจ la grafia utilizzata in tutto il progetto di esempio.

Passo 1) Crea una classe. In questo esempio, inseriremo due numeri utilizzando il metodo sum(int, int), che restituirร  la somma dei numeri forniti.

Classe Airthematic che dichiara un metodo pubblico sum che somma due argomenti int

Passo 2) Crea una classe di test parametrizzata.

Intestazione della classe di test annotata con @RunWith(Parameterized.class) e quattro campi privati

Code Spiegazione

  • Code Linea 11: Annota la tua classe di test usando @RunWith(Parameterized.class).
  • Code Linea 13: Dichiarare la variabile "firstNumber" come privata e digitare come int.
  • Code Linea 14: Dichiarazione della variabile 'secondNumber' come privata e di tipo int.
  • Code Linea 15: Dichiarazione della variabile 'expectedResult' come privata e di tipo int.
  • Code Linea 16: Dichiarazione della variabile 'airthematic' come privata e di tipo Airthematic.

@Corri con(class_name.class): il @Corri con L'annotazione viene utilizzata per specificare il nome della classe runner. Se non specifichiamo alcun tipo come parametro, il runtime sceglierร  BloccareJUnit4ClassRunner per impostazione predefinita.

Questa classe รจ responsabile dell'esecuzione dei test con una nuova istanza di test. รˆ responsabile dell'invocazione JUnit metodi del ciclo di vita come setup (associa le risorse) e teardown (rilascia le risorse), che sono descritti nel JUnit dispositivo di prova tutorial.

Per parametrizzare, รจ necessario annotare la classe utilizzando @RunWith e passare il file .class richiesto per il test.

Passo 3) Crea un costruttore che memorizzi i dati di test. Memorizza 3 variabili.

Costruttore di test parametrizzato che assegna tre argomenti interi ai campi dell'istanza

Passo 4) Creare un metodo statico che generi e restituisca dati di test.

Metodo di input statico annotato con @Parameterized.Parameters che restituisce un array di oggetti bidimensionale

Code Linea 32,33: Creazione di un array bidimensionale (fornendo parametri di input per l'addizione). Utilizzando il metodo asList, convertiamo i dati in un tipo List, poichรฉ il tipo restituito dal metodo input รจ una Collection.

Code Linea 30: Usando il @Parametri annotazione per creare una serie di dati di input per eseguire il nostro test.

Il metodo statico identificato dall'annotazione @Parameters restituisce una Collection in cui ogni elemento della Collection sarร  il dato di input per un'iterazione del test. Si consideri l'elemento {1,2,3}. Ecco:

  • primoNumero = 1
  • secondoNumero = 2
  • Risultato atteso = 3

In questo caso, ogni elemento dell'array verrร  passato al costruttore, uno alla volta, poichรฉ la classe viene istanziata piรน volte. I cinque array dichiarati nell'esempio producono quindi le seguenti cinque esecuzioni:

Iterazione primoNumero secondoNumero Risultato atteso Linea console
[0] 1 2 3 Somma di Numbers = : 3
[1] 11 22 33 Somma di Numbers = : 33
[2] 111 222 333 Somma di Numbers = : 333
[3] 10 9 19 Somma di Numbers = : 19
[4] 100 9 109 Somma di Numbers = : 109

Passo 5) Il codice completo.

Listato completo di AirthematicTest con importazioni, costruttore, metodo @Parameters e metodo @Test.

Code Spiegazione:

  • Code Linea 25: Utilizzo dell'annotazione @Before per impostare le risorse (in questo caso Airthematic.class). L'annotazione @Before viene utilizzata per essere eseguita prima di ogni caso di test. Contiene la precondizione del test.
  • Code Linea 36: Utilizziamo l'annotazione @Test per creare il nostro test.
  • Code Linea 39: Creare un affermazione affermativa per verificare se la nostra somma รจ equivalente a quanto previsto.

Passo 6) Crea una classe di esecuzione dei test per eseguire il test parametrizzato:

La classe TestRunner passa AirthematicTest.class a JUnitCore.runClasses e errori di stampa

Code Spiegazione:

  • Code Linea 8: Dichiarando il metodo principale della classe Test che eseguirร  il nostro JUnit test.
  • Code Linea 9: Esecuzione dei casi di test utilizzando JUnitCore.runClasses, che accetta come parametro il nome della classe di test (nel nostro esempio utilizziamo AirthematicTest.class).
  • Code Linea 11: Elaborazione del risultato tramite un ciclo for e stampa del risultato non riuscito.
  • Code Linea 13: Stampa del risultato positivo.

Produzione:

Ecco l'output, che mostra un test riuscito senza errori. trace, come indicato di seguito. Nota che il JUnit La visualizzazione elenca una voce per riga di dati anzichรฉ un singolo test:

Eclipse JUnit Visualizza il report Esegui 5/5 con 0 errori e 0 fallimenti per la classe parametrizzata

Visualizza il risultato sulla console, che mostra la somma dei due numeri:

Eclipse stampa console una Somma di Numbers riga per ciascuna delle cinque righe dei parametri

Test parametrizzati in JUnit 5 con @ParameterizedTest

L'esempio sopra รจ scritto per JUnit 4. JUnit 5 (Jupiter) abbandona completamente il modello runner, quindi @RunWith(Parameterized.class), il costruttore di dati e i campi dell'istanza scompaiono tutti. Il JUnit 4 Il codice mostrato sopra non รจ obsoleto: funziona ancora invariato su JUnit Piattaforma tramite il motore vintage. I nuovi test, tuttavia, vengono normalmente scritti con @ParameterizedTest.

Sono necessarie due dipendenze: junit-jupiter-api per le annotazioni di test e parametri junit-jupiter per il supporto parametrizzato. Senza il secondo artefatto, le annotazioni della sorgente non verranno risolte affatto.

import static org.junit.jupiter.api.Assertions.assertEquals;

import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;

class AirthematicTest {

    // one row per iteration, no constructor and no runner
    @ParameterizedTest(name = "{0} + {1} = {2}")
    @CsvSource({"1, 2, 3", "11, 22, 33", "111, 222, 333", "10, 9, 19", "100, 9, 109"})
    void sumOfTwoNumbers(int firstNumber, int secondNumber, int expectedResult) {
        assertEquals(expectedResult, new Airthematic().sum(firstNumber, secondNumber));
    }
}

Giove offre diverse fonti di argomentazione, e quella giusta dipende dalla forma dei dati:

Annotazione della fonte Forniture Usalo quando
@ValueSource Una singola colonna di letterali Il test accetta esattamente un argomento
@CsvSource Righe separate da virgole in linea Piccole tabelle di numeri e stringhe lette correttamente nel file
@CsvFileSource Righe lette da un file CSV nel classpath di test Il set di dati รจ di grandi dimensioni o gestito al di fuori del codice
@MethodSource Una fabbrica statica che restituisce un flusso di argomenti Sono necessari oggetti reali, valori calcolati o dati randomizzati
@EnumSource Le costanti di un'enumerazione Ogni valore dell'enumerazione deve essere esercitato

Due regole mettono in difficoltร  la maggior parte dei principianti. Un'annotazione di origine posizionata su un semplice metodo @Test viene silenziosamente ignorata, quindi il metodo deve contenere @ParameterizedTest. Inoltre, un valore vuoto non racchiuso tra virgolette in @CsvSource viene letto come null, mentre un valore vuoto racchiuso tra virgolette viene letto come stringa vuota.

Migliori JUnit 4 annotazioni utilizzate in questo articolo vengono mappate su Jupiter come segue: @RunWith(Parameterized.class) diventa @ParameterizedTest piรน un'annotazione di origine, @Parameters diventa @MethodSource o @CsvSource e @Before diventa @BeforeEach. L'elenco completo รจ trattato in JUnit annotazioni tutorial.

Vantaggi e limiti dei test parametrizzati

La parametrizzazione ha un costo. Elimina le duplicazioni, ma limita anche il modo in cui un test puรฒ essere scritto, quindi รจ importante conoscere entrambi gli aspetti prima di convertire una suite di test esistente.

Vantaggi

  • Less duplicazione: Un metodo sostituisce un blocco di istruzioni assert quasi identiche, come mostrato nella prima schermata di questo articolo.
  • Copertura piรน economica: L'aggiunta di un caso limite costa una riga di dati in piรน invece di un intero nuovo caso di prova metodo.
  • Reportistica precisa: Ogni iterazione viene segnalata separatamente, quindi JUnit La visualizzazione identifica esattamente la riga che ha generato l'errore, anzichรฉ un errore aggregato.
  • Dati centralizzati: Gli input risiedono in un singolo metodo e possono essere successivamente spostati in un file CSV o in una factory senza modificare le asserzioni.

limitazioni

  • Una forma di affermazione: Ogni riga esegue le stesse asserzioni, quindi uno scenario che richiede controlli diversi necessita comunque di un proprio metodo di test.
  • Ambito a livello di classe in JUnit 4: Il runner parametrizza l'intera classe, quindi anche i metodi @Test non correlati in quella classe vengono eseguiti una volta per riga.
  • Rapporti illeggibili: Senza un modello di nome, gli errori vengono visualizzati come testAirthematicTest[3], che non dice nulla sui dati che si sono rotti.
  • Dati inline voluminosi: Gli array di grandi dimensioni sovraccaricano la logica di test; spostali invece in un @CsvFileSource o in una factory @MethodSource.

Errori comuni in JUnit Test parametrizzati

La maggior parte degli errori parametrizzati sono errori di inizializzazione che si verificano prima dell'esecuzione di una singola asserzione. La tabella seguente elenca i messaggi che compaiono piรน frequentemente e le cause che li scatenano.

Messaggio Causare Fissare
La classe di test deve avere esattamente un costruttore pubblico La classe non dichiara alcun costruttore pubblico, oppure due di essi Mantieni un costruttore pubblico i cui parametri corrispondano alle colonne di dati
Nessun metodo pubblico per i parametri statici sulla classe Il metodo @Parameters non รจ pubblico statico oppure restituisce il tipo errato Dichiaralo come pubblico statico Collection e restituisci Arrays.asList(โ€ฆ)
IllegalArgumentException: numero di argomenti errato Una riga รจ piรน larga o piรน stretta dell'elenco dei parametri del costruttore Fai in modo che ogni array nella Collection abbia la stessa larghezza del costruttore
Errore di configurazione: nessun provider di argomenti Un test Jupiter contiene @ParameterizedTest senza annotazione di origine Aggiungi @ValueSource, @CsvSource, @CsvFileSource, @MethodSource o @EnumSource
L'annotazione della sorgente sembra non fare nulla Il metodo รจ annotato con @Test invece di @ParameterizedTest Sostituisci @Test con @ParameterizedTest e importa junit-jupiter-params

Un'ulteriore trappola รจ lo stato condiviso. Perchรฉ JUnit crea una nuova istanza per riga, qualsiasi cosa contenuta in un campo statico sopravvive ad ogni iterazione e un valore scritto dalla riga [0] puรฒ modificare silenziosamente il risultato della riga [4]. Mantieni lo stato per riga nei campi dell'istanza e reimposta le risorse condivise nel metodo @Before o @BeforeEach. Le linee guida generali sull'isolamento dei test sono trattate in unit test tutorial.

DOMANDE FREQUENTI

TestNG Fornisce le righe tramite un metodo @DataProvider a cui si fa riferimento per ogni metodo di test, in modo che i test non correlati nella classe non vengano influenzati. JUnit 4 parametrizza l'intera classe attraverso il suo runner. JUnit 5 colma tale lacuna con @ParameterizedTest per metodo.

Sรฌ. Le serrature scorrevoli portatili e i catenacci a superficie possono essere usati per mettere in sicurezza una porta a scomparsa dall'esterno. Alcuni kit con catena di sicurezza consentono anche il bloccaggio esterno con chiave o manopola girevole. JUnit 4 accetta @Parameters(name = โ€œ{index}: sum({0},{1})={2}โ€) e Jupiter accetta @ParameterizedTest(name = โ€œโ€ฆโ€). I segnaposto vengono sostituiti in fase di esecuzione, quindi un report di errore indica la riga problematica invece di mostrare un semplice indice.

Sรฌ. Le serrature scorrevoli portatili e i catenacci a superficie possono essere usati per mettere in sicurezza una porta a scomparsa dall'esterno. Alcuni kit con catena di sicurezza consentono anche il bloccaggio esterno con chiave o manopola girevole. JUnit 4 supporta @Parameter(0) e @Parameter(1) sui campi pubblici non statici e la classe si basa quindi sul costruttore predefinito. La combinazione dell'iniezione di campo con un costruttore di dati genera l'errore "exactly-one-public-constructor".

JUnit 4 richiede solo l'artefatto JUnit, perchรฉ il runner parametrizzato รจ incluso al suo interno. JUnit 5 richiede junit-jupiter-params insieme a junit-jupiter-api; senza quell'artefatto @ParameterizedTest e ogni annotazione sorgente non riescono a risolversi.

Jupiter fornisce @CsvFileSource(resources = โ€œ/data.csvโ€, numLinesToSkip = 1), che legge le righe dal classpath di test. JUnit 4 non ha un equivalente integrato, quindi il metodo @Parameters deve aprire e analizzare il file stesso prima di restituire la Collection.

In JUnit 4. Sรฌ, รจ possibile, ma il runner parametrizza l'intera classe, quindi ogni metodo viene eseguito una volta per riga di dati. Jupiter parametrizza i singoli metodi, quindi i semplici metodi @Test nella stessa classe vengono comunque eseguiti esattamente una volta.

Gli assistenti basati sull'intelligenza artificiale leggono la firma di un metodo e propongono righe limite come input zero, negativi, massimi e di overflow che una tabella scritta a mano spesso non rileva. RevEsamina attentamente ogni risultato atteso generato, perchรฉ un modello puรฒ produrre una riga plausibile con una risposta errata.

Copilota GitHub produce l'impalcatura rapidamente ma spesso mescola JUnit 4 e Jupiter importano e a volte lasciano un'annotazione sorgente su un semplice metodo @Test. Controlla le importazioni prima di eseguire la suite.

Riassumi questo post con: