SE VUOI PRENDERE LA CERTIFICAZIONE PER QUESTO CORSO CLICCA QUI
Gestione dell’esecuzione dei Test
Così come cargo run compila il tuo codice e poi esegue il binario risultante, cargo test compila il tuo codice in modalità test ed esegue il binario di test risultante. Il comportamento predefinito del binario prodotto da cargo test è quello di eseguire tutti i test in parallelo e catturare l’output generato durante l’esecuzione dei test, impedendo che l’output venga visualizzato e rendendo più facile leggere l’output relativo ai risultati dei test. Tuttavia, è possibile specificare delle opzioni da riga di comando per modificare questo comportamento predefinito.
Alcune opzioni da riga di comando vanno a cargo test, e altre vanno al binario di test risultante. Per separare questi due tipi di argomenti, si elencano gli argomenti che vanno a cargo test seguiti dal separatore — e poi quelli che vanno al binario di test. Eseguendo cargo test –help vengono visualizzate le opzioni che puoi utilizzare con cargo test, e eseguendo cargo test — –help vengono visualizzate le opzioni che puoi utilizzare dopo il separatore.
Esecuzione dei Test in Parallelo o in Sequenza
Quando si eseguono più test, di default vengono eseguiti in parallelo utilizzando thread, il che significa che finiscono di eseguire più velocemente e si ottiene un feedback più rapido. Poiché i test vengono eseguiti contemporaneamente, è necessario assicurarsi che i test non dipendano l’uno dall’altro o da uno stato condiviso, incluso un ambiente condiviso, come la directory di lavoro corrente o le variabili d’ambiente.
Ad esempio, supponiamo che ciascuno dei tuoi test esegua del codice che crea un file su disco chiamato test-output.txt e scriva dei dati in quel file. Quindi ogni test legge i dati in quel file e verifica che il file contenga un valore particolare, che è diverso in ogni test. Poiché i test vengono eseguiti contemporaneamente, un test potrebbe sovrascrivere il file nel tempo tra la scrittura e la lettura del file da parte di un altro test. Il secondo test fallirà quindi, non perché il codice è sbagliato ma perché i test si sono interferiti tra loro durante l’esecuzione in parallelo. Una soluzione è assicurarsi che ciascun test scriva su un file diverso; un’altra soluzione è eseguire i test uno alla volta.
Se non vuoi eseguire i test in parallelo o se desideri un controllo più fine sul numero di thread utilizzati, puoi inviare il flag –test-threads e il numero di thread che vuoi utilizzare al binario di test. Dai un’occhiata al seguente esempio:
sh
$ cargo test -- --test-threads=1
Abbiamo impostato il numero di thread dei test su 1, dicendo al programma di non utilizzare alcun parallelismo. Eseguire i test utilizzando un solo thread richiederà più tempo rispetto all’esecuzione in parallelo, ma i test non interferiranno tra loro se condivideranno lo stato.
Visualizzazione dell’Output delle Funzioni
Per default, se un test passa, la libreria di test di Rust cattura tutto ciò che viene stampato sull’output standard. Ad esempio, se chiamiamo println! in un test e il test passa, non vedremo l’output di println! nel terminale; vedremo solo la riga che indica il successo del test. Se un test fallisce, vedremo qualsiasi cosa sia stata stampata sull’output standard insieme al resto del messaggio di fallimento.
Come esempio, la Lista 11-10 ha una funzione sciocca che stampa il valore del suo parametro e restituisce 10, così come un test che passa e un test che fallisce.
rust
fn stampa_e_restituisce_10(a: i32) -> i32 {
println!("Ho ottenuto il valore {}", a);
10
} #[cfg(test)]
mod tests {
use super::*; #[test]
fn questo_test_passerà() {
let valore = stampa_e_restituisce_10(4);
assert_eq!(10, valore);
}
#[test]
fn questo_test_fallirà() {
let valore = stampa_e_restituisce_10(8);
assert_eq!(5, valore);
}
}
Quando eseguiamo questi test con cargo test, vedremo il seguente output:
sh
$ cargo test
Compiling silly-function v0.1.0 (file:///projects/silly-function)
Finished test [unoptimized + debuginfo] target(s) in 0.58s
Running unittests src/lib.rs (target/debug/deps/silly_function-160869f38cff9166) running 2 tests
test tests::this_test_will_fail ... FAILED
test tests::this_test_will_pass ... ok failures: ---- tests::this_test_will_fail stdout ----
I got the value 8
thread 'tests::this_test_will_fail' panicked at 'assertion failed: `(left == right)`
left: `5`,
right: `10`', src/lib.rs:19:9
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace failures:
tests::this_test_will_fail test result: FAILED. 1 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
error: test failed, to rerun pass `--lib`
Notare che in nessun punto di questo output vediamo “Ho ottenuto il valore 4”, che è ciò che viene stampato quando il test che passa viene eseguito. Quell’output è stato catturato. L’output del test che fallisce, “Ho ottenuto il valore 8”, appare nella sezione dell’output di riepilogo del test, che mostra anche la causa del fallimento del test.
Se vogliamo vedere i valori stampati anche per i test che passano, possiamo dire a Rust di mostrare anche l’output dei test riusciti con –show-output.
sh
$ cargo test -- --show-output
Quando eseguiamo di nuovo i test nella Lista 11-10 con il flag –show-output, vediamo il seguente output:
sh
$ cargo test -- --show-output
Compiling silly-function v0.1.0 (file:///projects/silly-function)
Finished test [unoptimized + debuginfo] target(s) in 0.60s
Running unittests src/lib.rs (target/debug/deps/silly_function-160869f38cff9166)
running 2 tests
test tests::this_test_will_fail ... FAILED
test tests::this_test_will_pass ... ok
successes:
---- tests::this_test_will_pass stdout ----
I got the value 4
successes:
tests::this_test_will_pass
failures:
---- tests::this_test_will_fail stdout ----
I got the value 8
thread 'tests::this_test_will_fail' panicked at 'assertion failed: `(left == right)`
left: `5`,
right: `10`', src/lib.rs:19:9
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
failures:
tests::this_test_will_fail
test result: FAILED. 1 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
error: test failed, to rerun pass `--lib`
Esecuzione di una Sottoinsieme di Test per Nome
A volte, eseguire l’intero insieme di test può richiedere molto tempo. Se stai lavorando su codice in una particolare area, potresti voler eseguire solo i test relativi a quel codice. Puoi scegliere quali test eseguire passando a cargo test il nome o i nomi del(i) test che desideri eseguire come argomento.
Per mostrare come eseguire una sottoinsieme di test, creeremo prima tre test per la nostra funzione add_two, come mostrato nella Lista 11-11, e sceglieremo quali eseguire.
rust
pub fn add_two(a: i32) -> i32 {
a + 2
} #[cfg(test)]
mod tests {
use super::*; #[test]
fn add_two_and_two() {
assert_eq!(4, add_two(2));
} #[test]
fn add_three_and_two() {
assert_eq!(5, add_two(3));
}
#[test]
fn one_hundred() {
assert_eq!(102, add_two(100));
}
}
Se eseguiamo i test senza passare alcun argomento, come visto in precedenza, tutti i test verranno eseguiti in parallelo:
sh
$ cargo test
Compiling adder v0.1.0 (file:///projects/adder)
Finished test [unoptimized + debuginfo] target(s) in 0.62s
Running unittests src/lib.rs (target/debug/deps/adder-92948b65e88960b4) running 3 tests
test tests::add_three_and_two ... ok
test tests::add_two_and_two ... ok
test tests::one_hundred ... ok test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s Doc-tests adder running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Esecuzione di Singoli Test
Possiamo passare il nome di qualsiasi funzione di test a cargo test per eseguire solo quel test:
sh
$ cargo test one_hundred
Compiling adder v0.1.0 (file:///projects/adder)
Finished test [unoptimized + debuginfo] target(s) in 0.69s
Running unittests src/lib.rs (target/debug/deps/adder-92948b65e88960b4) running 1 test
test tests::one_hundred ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 2 filtered out; finished in 0.00s
È stato eseguito solo il test con il nome one_hundred; gli altri due test non corrispondevano a quel nome. L’output dei test ci informa che ci sono stati più test che non sono stati eseguiti, visualizzando 2 filtered out alla fine.
Non possiamo specificare i nomi di più test in questo modo; solo il primo valore dato a cargo test verrà utilizzato. Ma c’è un modo per eseguire più test.
Filtraggio per Eseguire Più Test
Possiamo specificare parte del nome di un test, e verranno eseguiti tutti i test il cui nome corrisponde a quel valore. Ad esempio, poiché due dei nomi dei nostri test contengono add, possiamo eseguire quei due eseguendo cargo test add:
sh
$ cargo test add
Compiling adder v0.1.0 (file:///projects/adder)
Finished test [unoptimized + debuginfo] target(s) in 0.61s
Running unittests src/lib.rs (target/debug/deps/adder-92948b65e88960b4) running 2 tests
test tests::add_three_and_two ... ok
test tests::add_two_and_two ... ok
test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 1 filtered out; finished in 0.00s
Questo comando ha eseguito tutti i test con add nel nome e ha filtrato il test chiamato one_hundred. Nota anche che il modulo in cui appare un test diventa parte del nome del test, quindi possiamo eseguire tutti i test in un modulo filtrando sul nome del modulo.
Ignorare Alcuni Test a Meno Che Richiesti Esplicitamente
A volte alcuni test specifici possono richiedere molto tempo per essere eseguiti, quindi potresti volerli escludere durante la maggior parte delle esecuzioni di cargo test. Piuttosto che elencare come argomenti tutti i test che vuoi eseguire, puoi invece annotare i test che richiedono molto tempo utilizzando l’attributo ignore per escluderli, come mostrato qui:
rust
#[test]
fn it_works() {
assert_eq!(2 + 2, 4);
}
#[test]
#[ignore]
fn expensive_test() {
// codice che impiega un'ora per essere eseguito
}
Dopo #[test] aggiungiamo la linea #[ignore] al test che vogliamo escludere. Ora, quando eseguiamo i nostri test, it_works viene eseguito, ma expensive_test no:
sh
$ cargo test
Compiling adder v0.1.0 (file:///projects/adder)
Finished test [unoptimized + debuginfo] target(s) in 0.60s
Running unittests src/lib.rs (target/debug/deps/adder-92948b65e88960b4) running 2 tests
test expensive_test ... ignored
test it_works ... ok
test result: ok. 1 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out; finished in 0.00s
La funzione expensive_test è elencata come ignorata. Se vogliamo eseguire solo i test ignorati, possiamo usare cargo test — –ignored:
sh
$ cargo test -- --ignored
Compiling adder v0.1.0 (file:///projects/adder)
Finished test [unoptimized + debuginfo] target(s) in 0.61s
Running unittests src/lib.rs (target/debug/deps/adder-92948b65e88960b4) running 1 test
test expensive_test ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 1 filtered out; finished in 0.00s
Controllando quali test eseguire, puoi assicurarti che i risultati di cargo test saranno veloci. Quando sei in un punto in cui ha senso controllare i risultati dei test ignorati e hai tempo per attendere i risultati, puoi eseguire cargo test — –ignored invece. Se vuoi eseguire tutti i test, che siano ignorati o meno, puoi eseguire cargo test — –include-ignored.