Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Contrôler l’exécution des tests

Tout comme cargo run compilé votre code puis exécute le binaire resultant, cargo test compilé votre code en mode test et exécute le binaire de test resultant. Le comportement par défaut du binaire produit par cargo test est d’exécuter tous les tests en parallèle et de capturer la sortie générée pendant l’exécution des tests, empechant la sortie d’être affichée et facilitant la lecture de la sortie liee aux résultats des tests. Vous pouvez cependant spécifier des options en ligne de commande pour modifier ce comportement par défaut.

Certaines options en ligne de commande sont destinees a cargo test, et d’autres au binaire de test resultant. Pour séparer ces deux types d’arguments, vous listez les arguments destines a cargo test suivis du separateur -- puis ceux destines au binaire de test. Exécuter cargo test --help affiche les options utilisables avec cargo test, et exécuter cargo test -- --help affiche les options utilisables après le separateur. Ces options sont également documentees dans [la section “Tests” du livre The rustc Book][tests].

Exécuter les tests en parallèle ou consecutivement

Quand vous exécutez plusieurs tests, par défaut ils s’exécutent en parallèle en utilisant des threads, ce qui signifie qu’ils se terminent plus rapidement et que vous obtenez un retour plus tot. Comme les tests s’exécutent en même temps, vous devez vous assurer que vos tests ne dependent pas les uns des autres ni d’un état partage, y compris un environnement partage, comme le repertoire de travail courant ou les variables d’environnement.

Par exemple, imaginons que chacun de vos tests exécute du code qui crée un fichier sur le disque nomme test-output.txt et écrit des données dans ce fichier. Ensuite, chaque test lit les données de ce fichier et vérifie que le fichier contient une valeur particuliere, qui est différente dans chaque test. Comme les tests s’exécutent en même temps, un test pourrait ecraser le fichier entre le moment où un autre test écrit et lit le fichier. Le second test echouera alors, non pas parce que le code est incorrect mais parce que les tests se sont interferes mutuellement en s’exécutant en parallèle. Une solution est de s’assurer que chaque test écrit dans un fichier différent ; une autre solution est d’exécuter les tests un par un.

Si vous ne voulez pas exécuter les tests en parallèle ou si vous souhaitez un contrôle plus fin sur le nombre de threads utilises, vous pouvez envoyer le drapeau --test-threads et le nombre de threads que vous souhaitez utiliser au binaire de test. Regardez l’exemple suivant :

$ cargo test -- --test-threads=1

Nous avons défini le nombre de threads de test a 1, indiquant au programme de ne pas utiliser de parallelisme. Exécuter les tests avec un seul thread prendra plus de temps que de les exécuter en parallèle, mais les tests ne s’interfereront pas les uns avec les autres s’ils partagent un état.

Afficher la sortie des fonctions

Par défaut, si un test reussit, la bibliothèque de test de Rust capture tout ce qui est affiche sur la sortie standard. Par exemple, si nous appelons println! dans un test et que le test reussit, nous ne verrons pas la sortie de println! dans le terminal ; nous verrons seulement la ligne qui indique que le test a réussi. Si un test echoue, nous verrons tout ce qui a été affiche sur la sortie standard avec le reste du message d’échec.

A titre d’exemple, l’encart 11-10 contient une fonction simple qui affiche la valeur de son paramètre et retourné 10, ainsi qu’un test qui reussit et un test qui echoue.

Filename: src/lib.rs
fn prints_and_returns_10(a: i32) -> i32 {
    println!("I got the value {a}");
    10
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn this_test_will_pass() {
        let value = prints_and_returns_10(4);
        assert_eq!(value, 10);
    }

    #[test]
    fn this_test_will_fail() {
        let value = prints_and_returns_10(8);
        assert_eq!(value, 5);
    }
}
Listing 11-10: Tests for a function that calls println!

Quand nous exécutons ces tests avec cargo test, nous verrons la sortie suivante : console {{#include ../listings/ch11-writing-automated-tests/listing-11-10/output.txt}}

$ cargo test
   Compiling silly-function v0.1.0 (file:///projects/silly-function)
    Finished `test` profile [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 src/lib.rs:19:9:
assertion `left == right` failed
  left: 10
 right: 5
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`

Notez que nulle part dans cette sortie nous ne voyons I got the value 4, qui est affiche quand le test qui reussit s’exécute. Cette sortie a été capturee. La sortie du test qui a echoue, I got the value 8, apparaît dans la section du resume de sortie des tests, qui montre également la cause de l’échec du test.

Si nous voulons voir les valeurs affichées pour les tests qui reussissent également, nous pouvons demander a Rust d’afficher aussi la sortie des tests reussis avec --show-output :

$ cargo test -- --show-output

Quand nous exécutons les tests de l’encart 11-10 à nouveau avec le drapeau --show-output, nous voyons la sortie suivante : console {{#include ../listings/ch11-writing-automated-tests/output-only-01-show-output/output.txt}}

$ cargo test -- --show-output
   Compiling silly-function v0.1.0 (file:///projects/silly-function)
    Finished `test` profile [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 src/lib.rs:19:9:
assertion `left == right` failed
  left: 10
 right: 5
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`

Exécuter un sous-ensemble de tests par nom

Exécuter une suite de tests complète peut parfois prendre beaucoup de temps. Si vous travaillez sur du code dans un domaine particulier, vous pourriez vouloir n’exécuter que les tests relatifs à ce code. Vous pouvez choisir quels tests exécuter en passant a cargo test le nom ou les noms du ou des tests que vous souhaitez exécuter en argument.

Pour montrer comment exécuter un sous-ensemble de tests, nous allons d’abord créer trois tests pour notre fonction add_two, comme montre dans l’encart 11-11, et choisir lesquels exécuter.

Filename: src/lib.rs
pub fn add_two(a: u64) -> u64 {
    a + 2
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn add_two_and_two() {
        let result = add_two(2);
        assert_eq!(result, 4);
    }

    #[test]
    fn add_three_and_two() {
        let result = add_two(3);
        assert_eq!(result, 5);
    }

    #[test]
    fn one_hundred() {
        let result = add_two(100);
        assert_eq!(result, 102);
    }
}
Listing 11-11: Three tests with three different names

Si nous exécutons les tests sans passer d’arguments, comme nous l’avons vu precedemment, tous les tests s’exécuteront en parallèle : console {{#include ../listings/ch11-writing-automated-tests/listing-11-11/output.txt}}

$ cargo test
   Compiling adder v0.1.0 (file:///projects/adder)
    Finished `test` profile [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

Exécuter un seul test

Nous pouvons passer le nom de n’importe quelle fonction de test a cargo test pour n’exécuter que ce test : console {{#include ../listings/ch11-writing-automated-tests/output-only-02-single-test/output.txt}}

$ cargo test one_hundred
   Compiling adder v0.1.0 (file:///projects/adder)
    Finished `test` profile [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

Seul le test nomme one_hundred a été exécute ; les deux autres tests ne correspondaient pas à ce nom. La sortie des tests nous indique qu’il y avait d’autres tests qui n’ont pas été exécutés en affichant 2 filtered out à la fin.

Nous ne pouvons pas spécifier les noms de plusieurs tests de cette façon ; seule la première valeur donnée a cargo test sera utilisee. Mais il existe un moyen d’exécuter plusieurs tests.

Filtrer pour exécuter plusieurs tests

Nous pouvons spécifier une partie d’un nom de test, et tout test dont le nom correspond à cette valeur sera exécute. Par exemple, comme deux de nos noms de tests contiennent add, nous pouvons exécuter ces deux-la en lancant cargo test add : console {{#include ../listings/ch11-writing-automated-tests/output-only-03-multiple-tests/output.txt}}

$ cargo test add
   Compiling adder v0.1.0 (file:///projects/adder)
    Finished `test` profile [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

Cette commande a exécute tous les tests contenant add dans leur nom et a filtre le test nomme one_hundred. Notez également que le module dans lequel un test apparaît fait partie du nom du test, nous pouvons donc exécuter tous les tests d’un module en filtrant sur le nom du module.

Ignorer des tests sauf demande explicite

Parfois, quelques tests spécifiques peuvent être très longs a exécuter, vous pourriez donc vouloir les exclure lors de la plupart des exécutions de cargo test. Plutot que de lister en arguments tous les tests que vous souhaitez exécuter, vous pouvez annoter les tests chronophages avec l’attribut ignore pour les exclure, comme montre ici :

Fichier : src/lib.rs rust,noplayground {{#rustdoc_include ../listings/ch11-writing-automated-tests/listing-11-01/src/lib.rs}}

pub fn add(left: u64, right: u64) -> u64 {
    left + right
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn it_works() {
        let result = add(2, 2);
        assert_eq!(result, 4);
    }

    #[test]
    #[ignore]
    fn expensive_test() {
        // code that takes an hour to run
    }
}

Après #[test], nous ajoutons la ligne #[ignore] au test que nous voulons exclure. Maintenant, quand nous exécutons nos tests, it_works s’exécute, mais expensive_test ne s’exécute pas : console {{#include ../listings/ch11-writing-automated-tests/no-listing-11-ignore-a-test/output.txt}}

$ cargo test
   Compiling adder v0.1.0 (file:///projects/adder)
    Finished `test` profile [unoptimized + debuginfo] target(s) in 0.60s
     Running unittests src/lib.rs (target/debug/deps/adder-92948b65e88960b4)

running 2 tests
test tests::expensive_test ... ignored
test tests::it_works ... ok

test result: ok. 1 passed; 0 failed; 1 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

La fonction expensive_test est listee comme ignored. Si nous voulons n’exécuter que les tests ignores, nous pouvons utiliser cargo test -- --ignored : console {{#include ../listings/ch11-writing-automated-tests/output-only-04-running-ignored/output.txt}}

$ cargo test -- --ignored
   Compiling adder v0.1.0 (file:///projects/adder)
    Finished `test` profile [unoptimized + debuginfo] target(s) in 0.61s
     Running unittests src/lib.rs (target/debug/deps/adder-92948b65e88960b4)

running 1 test
test tests::expensive_test ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 1 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

En controlant quels tests s’exécutent, vous pouvez vous assurer que les résultats de cargo test seront retournés rapidement. Quand vous en etes à un point ou il est logique de vérifier les résultats des tests ignored et que vous avez le temps d’attendre les résultats, vous pouvez exécuter cargo test -- --ignored à la place. Si vous voulez exécuter tous les tests, qu’ils soient ignores ou non, vous pouvez exécuter cargo test -- --include-ignored.