Shopping cart

0

Cart

  • 0 item

Nessun prodotto nel carrello.

All categories
 Corso Gratuito di Programmazione Rust Lezione 037 – Come scrivere i test

SE VUOI PRENDERE LA CERTIFICAZIONE PER QUESTO CORSO CLICCA QUI

Come Scrivere Test

I test sono funzioni Rust che verificano che il codice non di test funzioni nel modo previsto. Le funzioni di test svolgono tipicamente queste tre azioni:

  1. Preparano i dati o lo stato necessario.
  2. Eseguono il codice che vuoi testare.
  3. Confrontano i risultati con le aspettative.

Esaminiamo le funzionalità che Rust fornisce specificamente per scrivere test che eseguono queste azioni, tra cui l’attributo test, alcuni macro e l’attributo should_panic.

Anatomia di una Funzione di Test

In Rust, un test è semplicemente una funzione annotata con l’attributo test. Le attribuzioni sono metadati su pezzi di codice Rust; un esempio è l’attributo derive che abbiamo usato con le strutture nel Capitolo 5. Per trasformare una funzione in una funzione di test, aggiungi #[test] sulla riga prima di fn. Quando esegui i tuoi test con il comando cargo test, Rust crea un eseguibile di test che esegue le funzioni annotate e segnala se ogni funzione di test passa o fallisce.

Ogni volta che creiamo un nuovo progetto di libreria con Cargo, viene generato automaticamente per noi un modulo di test con una funzione di test al suo interno. Questo modulo fornisce un modello per scrivere i tuoi test in modo da non dover cercare la struttura e la sintassi esatta ogni volta che inizi un nuovo progetto. Puoi aggiungere quante più funzioni di test aggiuntive e quanti più moduli di test desideri!

Esploreremo alcuni aspetti di come funzionano i test sperimentando con il modello di test prima di testare effettivamente del codice. Poi scriveremo dei test reali che chiamano del codice che abbiamo scritto e verificano che il suo comportamento sia corretto.

Creiamo un nuovo progetto di libreria chiamato adder che sommerà due numeri:

bash

$ cargo new adder --lib
Creato il progetto di libreria `adder`
$ cd adder

Il contenuto del file src/lib.rs nella tua libreria adder dovrebbe assomigliare a Listing 11-1.

rust

Filename: src/lib.rs

#[cfg(test)]
mod tests {
#[test]
fn it_works() {
let result = 2 + 2;
assert_eq!(result, 4);
}
}

Per ora, ignoriamo le prime due righe e concentriamoci sulla funzione. Nota l’annotazione #[test]: questo attributo indica che si tratta di una funzione di test, quindi il test runner sa di trattare questa funzione come un test. Potremmo anche avere funzioni non di test nel modulo di test per aiutare a preparare scenari comuni o eseguire operazioni comuni, quindi dobbiamo sempre indicare quali funzioni sono test.

Il corpo della funzione di esempio usa il macro assert_eq! per verificare che result, che contiene il risultato della somma di 2 e 2, sia uguale a 4. Questa affermazione serve come esempio del formato per un tipico test. Eseguiamolo per vedere che questo test passa.

Il comando cargo test esegue tutti i test nel nostro progetto, come mostrato in Listing 11-2.

bash

$ cargo test
Compilazione in corso di adder v0.1.0 (file:///projects/adder)
Finito test [non ottimizzato + informazioni di debug] in 0.57s
Esecuzione dei test di unità src/lib.rs (target/debug/deps/adder-92948b65e88960b4)
esecuzione di 1 test
test tests::it_works ... ok risultato del test: ok. 1 superato; 0 fallito; 0 ignorato; 0 misurato; 0 filtrato; finito in 0.00s Test di documentazione adder esecuzione di 0 test

risultato del test: ok. 0 superato; 0 fallito; 0 ignorato; 0 misurato; 0 filtrato; finito in 0.00s

Cargo ha compilato ed eseguito il test. Vediamo la riga che esegue 1 test. La riga successiva mostra il nome della funzione di test generata, chiamata it_works, e che il risultato di quel test è ok. Il sommario generale del risultato del test: ok. significa che tutti i test sono passati e la parte che legge 1 passato; 0 fallito totalizza il numero di test passati o falliti.

È possibile contrassegnare un test come ignorato in modo che non venga eseguito in un’istanza particolare; affronteremo questo argomento nella sezione “Ignorare alcuni test a meno che non siano richiesti esplicitamente” più avanti in questo capitolo. Poiché non lo abbiamo fatto qui, il sommario mostra 0 ignorato. Possiamo anche passare un argomento al comando cargo test per eseguire solo i test il cui nome corrisponde a una stringa; questo è chiamato filtraggio e lo affronteremo nella sezione “Esecuzione di un sottoinsieme di test per nome”. Non abbiamo neanche filtrato i test in esecuzione, quindi alla fine del sommario mostra 0 filtrato.

La statistica 0 misurata è per i test delle prestazioni. I test delle prestazioni sono, al momento della stesura, disponibili solo in Rust nightly. Consulta la documentazione sui test delle prestazioni per saperne di più.

La parte successiva dell’output del test a partire da Test di documentazione adder è per i risultati di eventuali test di documentazione. Non abbiamo ancora test di documentazione, ma Rust può compilare qualsiasi esempio di codice che appare nella nostra documentazione API. Questa funzionalità aiuta a mantenere la documentazione e il codice sincronizzati! Discuteremo di come scrivere test di documentazione nella sezione “Commenti di documentazione come test” del Capitolo 14. Per ora, ignoriamo l’output dei test di documentazione.

Iniziamo a personalizzare il test secondo le nostre esigenze. Cambia prima il nome della funzione it_works in un nome diverso, come esplorazione, in questo modo:

rust

Filename: src/lib.rs

#[cfg(test)]
mod tests {
#[test]
fn esplorazione() {
assert_eq!(2 + 2, 4);
}
}

Poi esegui nuovamente cargo test. L’output mostra ora esplorazione invece di it_works:

bash

$ cargo test
Compilazione in corso di adder v0.1.0 (file:///projects/adder)
Finito test [non ottimizzato + informazioni di debug] in 0.59s
Esecuzione dei test di unità src/lib.rs (target/debug/deps/adder-92948b65e88960b4)
esecuzione di 1 test
test tests::esplorazione ... ok risultato del test: ok. 1 superato; 0 fallito; 0 ignorato; 0 misurato; 0 filtrato; finito in 0.00s Test di documentazione adder esecuzione di 0 test

risultato del test: ok. 0 superato; 0 fallito; 0 ignorato; 0 misurato; 0 filtrato; finito in 0.00s

Ora aggiungiamo un altro test, ma questa volta creiamo un test che fallisce! I test falliscono quando qualcosa nella funzione di test genera un’eccezione. Ogni test viene eseguito in un nuovo thread e quando il thread principale vede che un thread di test è morto, il test viene contrassegnato come fallito. Nel Capitolo 9, abbiamo parlato del modo più semplice per generare un’eccezione, chiamando il macro panic!. Inseriamo il nuovo test come una funzione chiamata un altro, in modo che il file src/lib.rs assomigli a Listing 11-3.

rust

Filename: src/lib.rs [Questo codice genera un'eccezione!]
#[cfg(test)]
mod tests {
#[test]
fn esplorazione() {
assert_eq!(2 + 2, 4);
}

#[test]
fn un_altro() {
panic!("Fai fallire questo test");
}
}

Esegui nuovamente i test usando cargo test. L’output dovrebbe essere simile a Listing 11-4, che mostra che il nostro test di esplorazione è passato e un altro è fallito.

bash

$ cargo test
Compilazione in corso di adder v0.1.0 (file:///projects/adder)
Finito test [non ottimizzato + informazioni di debug] in 0.72s
Esecuzione dei test di unità src/lib.rs (target/debug/deps/adder-92948b65e88960b4)
esecuzione di 2 test
test tests::un_altro ... FAILED
test tests::esplorazione ... ok fallimenti: ---- tests::un_altro stdout ----
thread 'tests::un_altro' panicked at 'Fai fallire questo test', src/lib.rs:10:9
nota: eseguire con la variabile d'ambiente `RUST_BACKTRACE=1` per visualizzare un traceback fallimenti:
tests::un_altro risultato del test: FAILED. 1 superato; 1 fallito; 0 ignorato; 0 misurato; 0 filtrato; finito in 0.00s

errore: test fallito, per eseguire di nuovo passare `--lib`

Invece di ok, la linea test tests::un_altro mostra FALLITO. Tra i risultati individuali e il sommario appaiono due nuove sezioni: la prima visualizza il motivo dettagliato di ogni fallimento del test. In questo caso, otteniamo i dettagli che un_altro è fallito perché ha generato un’eccezione con ‘Fai fallire questo test’ alla riga 10 nel file src/lib.rs. La sezione successiva elenca solo i nomi di tutti i test falliti, il che è utile quando ci sono molti test e molti dettagliati output dei test falliti. Possiamo usare il nome di un test fallito per eseguire solo quel test in modo da poterlo debuggare più facilmente; parleremo di più modi per eseguire i test nella sezione “Controllare come vengono eseguiti i test”.

La linea di riepilogo viene visualizzata alla fine: complessivamente, il nostro risultato del test è FALLITO. Abbiamo superato un test e ne abbiamo fallito uno.

Ora che hai visto com’è l’output dei test in diverse situazioni, vediamo alcuni macro diversi da panic! che sono utili nei test.

Controllo dei Risultati con il Macro assert!

Il macro assert!, fornito dalla libreria standard, è utile quando desideri assicurarti che una certa condizione in un test si valuti a vero. Passiamo al macro assert! un argomento che si valuta a un Booleano. Se il valore è vero, non succede nulla e il test passa. Se il valore è falso, il macro assert! chiama panic! per far fallire il test. Utilizzare il macro assert! ci aiuta a verificare che il nostro codice funzioni nel modo desiderato.

Nel Capitolo 5, Lista 5-15, abbiamo usato una struttura Rectangle e un metodo can_hold, che sono ripetuti qui nella Lista 11-5. Mettiamo questo codice nel file src/lib.rs, poi scriviamo alcuni test per esso utilizzando il macro assert!.

Utilizzo del Struct Rectangle e del metodo can_hold dal Capitolo 5

Il metodo can_hold restituisce un Booleano, il che significa che è un caso d’uso perfetto per il macro assert!. Nella Lista 11-6, scriviamo un test che esegue il metodo can_hold creando un’istanza Rectangle con una larghezza di 8 e un’altezza di 7 e affermando che può contenere un’altra istanza Rectangle con una larghezza di 5 e un’altezza di 1.

Un test per can_hold che verifica se un rettangolo più grande può effettivamente contenere un rettangolo più piccolo

Nota che abbiamo aggiunto una nuova riga all’interno del modulo tests: use super::*;. Il modulo tests è un modulo regolare che segue le normali regole di visibilità che abbiamo coperto nel Capitolo 7 nella sezione “Percorsi per fare riferimento a un elemento nell’albero dei moduli”. Poiché il modulo tests è un modulo interno, dobbiamo portare il codice sotto test nel modulo esterno nello scope del modulo interno. Usiamo un glob qui in modo che tutto ciò che definiamo nel modulo esterno sia disponibile per questo modulo di test.

Abbiamo chiamato il nostro test larger_can_hold_smaller e abbiamo creato le due istanze Rectangle di cui abbiamo bisogno. Poi abbiamo chiamato il macro assert! e gli abbiamo passato il risultato della chiamata larger.can_hold(&smaller). Questa espressione dovrebbe restituire true, quindi il nostro test dovrebbe passare.

bash

$ cargo test
Compilazione in corso di rectangle v0.1.0 (file:///projects/rectangle)
Finito test [non ottimizzato + debuginfo] in 0.66s
Esecuzione dei test di unità src/lib.rs (target/debug/deps/rectangle-6584c4561e48942e)
esecuzione di 1 test
test tests::larger_can_hold_smaller ... ok

risultato del test: ok. 1 superato; 0 fallito; 0 ignorato; 0 misurato; 0 filtrato; finito in 0.00s

Passa! Aggiungiamo un altro test, stavolta affermando che un rettangolo più piccolo non può contenere un rettangolo più grande:

rust

#[cfg(test)]
mod tests {
use super::*;
#[test]
fn larger_can_hold_smaller() {
// --snip--
} #[test]
fn smaller_cannot_hold_larger() {
let larger = Rectangle {
width: 8,
height: 7,
};
let smaller = Rectangle {
width: 5,
height: 1,
};

assert!(!smaller.can_hold(&larger));
}
}

Poiché il risultato corretto della funzione can_hold in questo caso è false, dobbiamo negare quel risultato prima di passarlo al macro assert!. Di conseguenza, il nostro test passerà se can_hold restituirà false:

bash

$ cargo test
Compilazione in corso di rectangle v0.1.0 (file:///projects/rectangle)
Finito test [non ottimizzato + debuginfo] in 0.66s
Esecuzione dei test di unità src/lib.rs (target/debug/deps/rectangle-6584c4561e48942e)
esecuzione di 2 test
test tests::larger_can_hold_smaller ... ok
test tests::smaller_cannot_hold_larger ... ok

risultato del test: ok. 2 superati; 0 falliti; 0 ignorati; 0 misurati; 0 filtrati; finito in 0.00s

Due test che passano! Ora vediamo cosa succede ai nostri risultati dei test quando introduciamo un bug nel nostro codice. Cambieremo l’implementazione del metodo can_hold sostituendo il segno di maggiore con un segno di minore quando confronta le larghezze:

[Questo codice non produce il comportamento desiderato.]

Eseguendo i test ora produce quanto segue:

bash

$ cargo test
Compilazione in corso di rectangle v0.1.0 (file:///projects/rectangle)
Finito test [non ottimizzato + debuginfo] in 0.66s
Esecuzione dei test di unità src/lib.rs (target/debug/deps/rectangle-6584c4561e48942e)
esecuzione di 2 test
test tests::larger_can_hold_smaller ... FALLITO
test tests::smaller_cannot_hold_larger ... ok fallimenti: ---- tests::larger_can_hold_smaller stdout ----
thread 'tests::larger_can_hold_smaller' panicked at 'assertion failed: larger.can_hold(&smaller)', src/lib.rs:28:9
nota: eseguire con la variabile d'ambiente `RUST_BACKTRACE=1` per visualizzare un traceback fallimenti:
tests::larger_can_hold_smaller risultato del test: FALLITO. 1 superato; 1 fallito; 0 ignorato; 0 misurati; 0 filtrati; finito in 0.00s

errore: test fallito, per eseguire di nuovo passare `--lib`

I nostri test hanno scoperto il bug! Poiché larger.width è 8 e smaller.width è 5, il confronto delle larghezze in can_hold ora restituisce false: 8 non è minore di 5.

Corso su Test di Uguaglianza con i Macro assert_eq! e assert_ne!

Un modo comune per verificare la funzionalità è testare l’uguaglianza tra il risultato del codice in esame e il valore che ci si aspetta. Si potrebbe fare ciò usando il macro assert! e passando un’espressione che utilizza l’operatore ==. Tuttavia, questo è un test così comune che la libreria standard fornisce un paio di macro—assert_eq! e assert_ne!—per eseguire questo test in modo più comodo. Questi macro confrontano due argomenti per uguaglianza o disuguaglianza, rispettivamente. Stamperanno anche i due valori se l’asserzione fallisce, rendendo più facile capire il motivo del fallimento del test; al contrario, il macro assert! indica solo che ha ottenuto un valore falso per l’espressione ==, senza stampare i valori che hanno portato al valore falso.

Nel Codice 11-7, scriviamo una funzione chiamata add_two che aggiunge 2 al suo parametro, poi testiamo questa funzione usando il macro assert_eq!.

rust

pub fn add_two(a: i32) -> i32 {
a + 2
}
#[cfg(test)]
mod tests {
use super::*;

#[test]
fn it_adds_two() {
assert_eq!(4, add_two(2));
}
}

Verifichiamo che passi!

bash

$ cargo test
Compilazione in corso adder v0.1.0 (file:///projects/adder)
Compilazione target non ottimizzato + debuginfo] terminata in 0.58s
Esecuzione test unità src/lib.rs (target/debug/deps/adder-92948b65e88960b4)
esecuzione 1 test
test tests::it_adds_two ... ok risultato test: ok. 1 superato; 0 fallito; 0 ignorato; 0 misurato; 0 filtrato; terminato in 0.00s Test di documentazione adder esecuzione 0 test

risultato test: ok. 0 superato; 0 fallito; 0 ignorato; 0 misurato; 0 filtrato; terminato in 0.00s

Passiamo il numero 4 come argomento a assert_eq!, che è uguale al risultato della chiamata add_two(2). La riga per questo test è test tests::it_adds_two … ok, e il test ha superato!

Introduciamo un bug nel nostro codice per vedere come appare assert_eq! quando fallisce. Cambiamo l’implementazione della funzione add_two per aggiungere invece 3:

rust

pub fn add_two(a: i32) -> i32 {
a + 3
}

Eseguiamo di nuovo i test:

less

$ cargo test
Compilazione in corso adder v0.1.0 (file:///projects/adder)
Compilazione target non ottimizzato + debuginfo] terminata in 0.61s
Esecuzione test unità src/lib.rs (target/debug/deps/adder-92948b65e88960b4)
esecuzione 1 test
test tests::it_adds_two ... FALLITO fallimenti: ---- tests::it_adds_two stdout ----
thread 'tests::it_adds_two' panicked at 'assertion failed: `(left == right)`
left: `4`,
right: `5`', src/lib.rs:11:9
nota: esegui con `RUST_BACKTRACE=1` variabile d'ambiente per visualizzare una traccia dello stack fallimenti:
tests::it_adds_two risultato test: FALLITO. 0 superato; 1 fallito; 0 ignorato; 0 misurato; 0 filtrato; terminato in 0.00s

errore: test fallito, per eseguire di nuovo passare `--lib`

Il nostro test ha individuato il bug! Il test it_adds_two è fallito, e il messaggio ci dice che l’asserzione che fallisce era assertion failed: (left == right) e quali sono i valori di left e right. Questo messaggio aiuta a iniziare il debug: l’argomento left era 4 ma l’argomento right, dove avevamo add_two(2), era 5. Questo sarebbe particolarmente utile quando abbiamo molti test in corso.

Aggiunta di Messaggi di Fallimento Personalizzati

Puoi anche aggiungere un messaggio personalizzato da stampare insieme al messaggio di fallimento come argomenti opzionali ai macro assert!, assert_eq!, e assert_ne!. Qualsiasi argomento specificato dopo gli argomenti richiesti viene passato al macro format!, quindi puoi passare una stringa di formato che contiene segnaposto {} e valori da inserire in quei segnaposto. I messaggi personalizzati sono utili per documentare cosa significa un’asserzione; quando un test fallisce, avrai una migliore idea del problema con il codice.

Ad esempio, diciamo che abbiamo una funzione che saluta le persone per nome e vogliamo testare che il nome che passiamo alla funzione appaia nell’output:

rust

pub fn greeting(name: &str) -> String {
format!("Hello {}!", name)
}
#[cfg(test)]
mod tests {
use super::*;

#[test]
fn greeting_contains_name() {
let result = greeting("Carol");
assert!(result.contains("Carol"));
}
}

I requisiti per questo programma non sono stati ancora concordati, e siamo abbastanza sicuri che il testo Hello all’inizio del saluto cambierà. Abbiamo deciso che non vogliamo dover aggiornare il test quando i requisiti cambiano, quindi invece di controllare l’uguaglianza esatta al valore restituito dalla funzione di saluto, affermeremo solo che l’output contiene il testo del parametro di input.

Ora introduciamo un bug in questo codice cambiando il saluto per escludere il nome e vedere come appare il fallimento predefinito del test:

rust

pub fn greeting(name: &str) -> String {
String::from("Hello!")
}

Eseguendo questo test otteniamo quanto segue:

less

$ cargo test
Compilazione in corso greeter v0.1.0 (file:///projects/greeter)
Terminata compilazione target di test non ottimizzato + debuginfo] in 0.91s
Esecuzione test unità src/lib.rs (target/debug/deps/greeter-170b942eb5bf5e3a)
esecuzione 1 test
test tests::greeting_contains_name ... FALLITO fallimenti: ---- tests::greeting_contains_name stdout ----
thread 'tests::greeting_contains_name' panicked at 'assertion failed: result.contains(\"Carol\")', src/lib.rs:12:9
nota: esegui con `RUST_BACKTRACE=1` per visualizzare una traccia dello stack fallimenti:
tests::greeting_contains_name risultato test: FALLITO. 0 superato; 1 fallito; 0 ignorato; 0 misurato; 0 filtrato; terminato in 0.00s errore: test failed, to rerun pass `--lib` Questo risultato indica semplicemente che l'asserzione è fallita e su quale riga si trova l'asserzione. Un messaggio di fallimento più utile stamperebbe il valore della funzione di saluto. Aggiungiamo ora un messaggio di fallimento personalizzato composto da una stringa di formato con un segnaposto riempito con il valore effettivo ottenuto dalla funzione di saluto:

```rust
#[test]
fn greeting_contains_name() {
let result = greeting("Carol");
assert!(
result.contains("Carol"),
"Il saluto non contiene il nome, il valore era `{}`",
result
);
}

Ora quando eseguiamo il test, otteniamo un messaggio di errore più informativo:

less

$ cargo test
Compilazione in corso greeter v0.1.0 (file:///projects/greeter)
Terminata compilazione target di test non ottimizzato + debuginfo] in 0.93s
Esecuzione test unità src/lib.rs (target/debug/deps/greeter-170b942eb5bf5e3a)
esecuzione 1 test
test tests::greeting_contains_name ... FALLITO fallimenti: ---- tests::greeting_contains_name stdout ----
thread 'tests::greeting_contains_name' panicked at 'Il saluto non contiene il nome, il valore era `Hello!`', src/lib.rs:12:9
nota: esegui con `RUST_BACKTRACE=1` per visualizzare una traccia dello stack fallimenti:
tests::greeting_contains_name risultato test: FALLITO. 0 superato; 1 fallito; 0 ignorato; 0 misurato; 0 filtrato; terminato in 0.00s errore: test failed, to rerun pass `--lib`

Possiamo vedere il valore effettivo ottenuto nell'output del test, il che ci aiuterebbe a eseguire il debug di quanto è accaduto anziché di ciò che ci aspettavamo che accadesse.

Controllo dei Panici con should_panic

Oltre a controllare i valori restituiti, è importante verificare che il nostro codice gestisca le condizioni di errore come ci aspettiamo. Ad esempio, consideriamo il tipo Guess che abbiamo creato nel Capitolo 9. Altri codici che utilizzano Guess dipendono dalla garanzia che le istanze di Guess contengano solo valori compresi tra 1 e 100. Possiamo scrivere un test che assicura che il tentativo di creare un’istanza di Guess con un valore al di fuori di tale intervallo provochi un panico.

Lo facciamo aggiungendo l’attributo should_panic alla nostra funzione di test. Il test passa se il codice all’interno della funzione provoca un panico; il test fallisce se il codice all’interno della funzione non provoca un panico.

Ecco un esempio di test che controlla che le condizioni di errore di Guess::new si verifichino quando ci aspettiamo.

rust

pub struct Guess {
value: i32,
}
impl Guess {
pub fn new(value: i32) -> Guess {
if value < 1 || value > 100 {
panic!("Il valore di Guess deve essere compreso tra 1 e 100, ottenuto {}.", value);
} Guess { value }
}
} #[cfg(test)]
mod tests {
use super::*;

#[test]
#[should_panic]
fn maggiore_di_100() {
Guess::new(200);
}
}

Posizioniamo l’attributo #[should_panic] dopo l’attributo #[test] e prima della funzione di test a cui si applica. Vediamo il risultato quando questo test passa:

bash

$ cargo test
Compilazione in corso guessing_game v0.1.0 (file:///projects/guessing_game)
Terminata compilazione target di test non ottimizzato + debuginfo] in 0.58s
Esecuzione test unità src/lib.rs (target/debug/deps/guessing_game-57d70c3acb738f4d)
esecuzione 1 test
test tests::maggiore_di_100 - should panic ... ok risultato test: ok. 1 superato; 0 fallito; 0 ignorato; 0 misurato; 0 filtrato; terminato in 0.00s Doc-tests guessing_game esecuzione 0 test risultato test: ok. 0 superato; 0 fallito; 0 ignorato; 0 misurato; 0 filtrato; terminato in 0.00s Sembra buono! Ora introduciamo un bug nel nostro codice rimuovendo la condizione che la nuova funzione provocherà un panico se il valore è maggiore di 100: ```rust
pub fn new(value: i32) -> Guess {
if value < 1 {
panic!("Il valore di Guess deve essere compreso tra 1 e 100, ottenuto {}.", value);
}

Guess { value }
}

Quando eseguiamo il test, fallirà:

less

$ cargo test
Compilazione in corso guessing_game v0.1.0 (file:///projects/guessing_game)
Terminata compilazione target di test non ottimizzato + debuginfo] in 0.62s
Esecuzione test unità src/lib.rs (target/debug/deps/guessing_game-57d70c3acb738f4d)
esecuzione 1 test
test tests::maggiore_di_100 - should panic ... FALLITO fallimenti: ---- tests::maggiore_di_100 stdout ----
nota: il test non ha panico come previsto fallimenti:
tests::maggiore_di_100 risultato test: FALLITO. 0 superato; 1 fallito; 0 ignorato; 0 misurato; 0 filtrato; terminato in 0.00s

errore: test failed, to rerun pass `--lib`

In questo caso non otteniamo un messaggio di errore molto utile, ma quando guardiamo la funzione di test, vediamo che è annotata con #[should_panic]. Il fallimento ottenuto significa che il codice nella funzione di test non ha causato un panico.

I test che utilizzano should_panic possono essere imprecisi. Un test should_panic passerebbe anche se il test provocasse un panico per un motivo diverso da quello che ci aspettiamo. Per rendere i test should_panic più precisi, possiamo aggiungere un parametro opzionale expected all’attributo should_panic. Il sistema di test garantirà che il messaggio di errore contenga il testo fornito. Ad esempio, considera il codice modificato per Guess in cui la nuova funzione provoca panici con messaggi diversi a seconda che il valore sia troppo piccolo o troppo grande.

Utilizzo di Result<T, E> nei Test

Fino ad ora i nostri test generano un panico quando falliscono. Possiamo anche scrivere test che utilizzano Result<T, E>! Ecco il test dalla Lista 11-1, riscritto per utilizzare Result<T, E> e restituire un Err invece di generare un panico:

rust

#[cfg(test)]
mod tests {
#[test]
fn funziona() -> Result<(), String> {
if 2 + 2 == 4 {
Ok(())
} else {
Err(String::from("due più due non è uguale a quattro"))
}
}
}

La funzione funziona ora ha il tipo di ritorno Result<(), String>. Nel corpo della funzione, anziché chiamare il macro assert_eq!, restituiamo Ok(()) quando il test ha successo e Err con una String al suo interno quando il test fallisce.

Scrivere i test in modo che restituiscano un Result<T, E> ti consente di utilizzare l’operatore ? nel corpo dei test, che può essere un modo conveniente per scrivere test che dovrebbero fallire se una qualsiasi operazione al loro interno restituisce una variante Err.

Non puoi utilizzare l’annotazione #[should_panic] sui test che utilizzano Result<T, E>. Per asserire che un’operazione restituisca una variante Err, non utilizzare l’operatore ? sul valore Result<T, E>. Invece, utilizza assert!(valore.is_err()).

Ora che conosci diversi modi per scrivere test, vediamo cosa succede quando eseguiamo i nostri test e esploriamo le diverse opzioni che possiamo utilizzare con cargo test.

1 Comment

Leave a Reply

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *