Contactez-nous

Lancer les tests avec `cargo test`

Apprenez à utiliser la commande `cargo test` pour exécuter vos tests unitaires et d'intégration en Rust, filtrer les tests et interpréter les résultats pour un débogage efficace.

`cargo test` : votre interface unique pour l'exécution des tests en Rust

Une fois que vous avez pris le soin d'écrire des tests pour votre code Rust, que ce soit des tests unitaires co-localisés avec votre code source ou des tests d'intégration dans un répertoire `tests` dédié, vous avez besoin d'un moyen simple et efficace pour les exécuter. C'est là qu'intervient la commande `cargo test`. Intégrée à Cargo, le gestionnaire de paquets et système de build de Rust, `cargo test` est l'outil centralisé pour compiler votre code en mode test et lancer toutes les fonctions de test identifiées.

L'utilisation de `cargo test` simplifie considérablement le processus d'exécution des tests. Elle s'occupe de découvrir les tests, de les compiler, de les exécuter (par défaut en parallèle pour gagner du temps), et de rapporter les résultats de manière claire et concise. Comprendre comment utiliser `cargo test` et interpréter sa sortie est fondamental pour intégrer les tests de manière productive dans votre cycle de développement.

Cette commande ne se limite pas à une simple exécution globale ; elle offre également des options pour contrôler quels tests sont exécutés, comment ils sont exécutés, et le niveau de détail de la sortie, ce qui la rend adaptable à divers besoins, du développement local rapide aux pipelines d'intégration continue.

Exécution de base et interprétation des résultats de `cargo test`

La manière la plus simple d'exécuter tous les tests de votre projet est de naviguer jusqu'au répertoire racine de votre crate et d'exécuter la commande :

cargo test

Lorsque vous lancez cette commande, Cargo effectue les actions suivantes :

  1. Il compile votre code source, y compris les modules annotés avec `#[cfg(test)]` et toutes les fonctions marquées avec `#[test]`. Si votre projet est une bibliothèque, il la compile comme une bibliothèque. S'il contient des exécutables, ils peuvent aussi être compilés si des tests y sont associés.
  2. Il compile également tout code se trouvant dans le répertoire `tests` à la racine de votre projet (tests d'intégration), chaque fichier étant traité comme un crate de test distinct.
  3. Il exécute ensuite toutes les fonctions de test découvertes. Par défaut, ces tests sont lancés sur plusieurs threads pour accélérer l'exécution.
  4. Enfin, il affiche un résumé des résultats.

La sortie typique de `cargo test` lorsque tous les tests réussissent ressemble à ceci :

   Compiling mon_projet v0.1.0 (/chemin/vers/mon_projet)
    Finished test [unoptimized + debuginfo] target(s) in 0.50s
     Running unittests (target/debug/deps/mon_projet-somerandomhash)

running 3 tests
test mon_module::tests::test_fonction_a ... ok
test mon_module::tests::test_fonction_b ... ok
test tests::integration_test_xyz ... ok

test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.01s

     Running doctests mon_projet

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

Cette sortie indique :

  • La compilation a réussi.
  • `running 3 tests` : Nombre total de tests unitaires et d'intégration exécutés.
  • Pour chaque test, son nom complet (module::sous_module::nom_test) et son statut (`ok` pour réussi).
  • Un résumé : `test result: ok. 3 passed; 0 failed; ...`.
  • `Running doctests` : Cargo exécute également les tests présents dans la documentation (doctests), s'il y en a.

Si un test échoue, la sortie sera différente. Par exemple, si `test_fonction_b` échoue à cause d'une assertion `assert_eq!(2+2, 5);` :

running 3 tests
test mon_module::tests::test_fonction_a ... ok
test mon_module::tests::test_fonction_b ... FAILED
test tests::integration_test_xyz ... ok

failures:

---- mon_module::tests::test_fonction_b stdout ----
thread 'mon_module::tests::test_fonction_b' panicked at 'assertion failed: `(left == right)`
  left: `4`,
 right: `5`', src/mon_module.rs:25:9
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace


failures:
    mon_module::tests::test_fonction_b

test result: FAILED. 2 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.01s

error: test failed, to rerun pass '--lib'

Ici, `cargo test` signale clairement :

  • Le test `mon_module::tests::test_fonction_b` a échoué (`FAILED`).
  • La section `failures` détaille l'échec : la cause (panique due à une assertion), le message d'erreur de l'assertion (avec les valeurs `left` et `right`), et l'emplacement exact (fichier et ligne).
  • Le résumé final indique `1 failed`.

Cette information est cruciale pour localiser et corriger rapidement le bug.

Filtrer les tests à exécuter avec `cargo test`

Dans les projets de grande taille, exécuter l'intégralité de la suite de tests peut prendre du temps. `cargo test` permet de filtrer les tests à exécuter en passant un argument de filtrage après la commande. Cet argument est une chaîne de caractères, et seuls les tests dont le nom complet (incluant les noms de modules) contient cette chaîne seront exécutés.

Par exemple, pour n'exécuter que les tests contenant le mot `fonction_a` dans leur nom :

cargo test fonction_a

Si vous avez un test nommé `mon_module::tests::test_fonction_a` et un autre `autre_module::tests::verifier_fonction_a_special`, les deux seront exécutés.

Pour être plus spécifique et cibler un test précis par son nom exact :

cargo test mon_module::tests::test_fonction_a

Vous pouvez également filtrer par nom de module pour exécuter tous les tests d'un module spécifique :

cargo test mon_module::tests

Cette capacité de filtrage est très utile pendant le développement, car elle permet de se concentrer sur les tests relatifs à la partie du code sur laquelle vous travaillez actuellement, offrant un cycle de feedback plus rapide.

Options avancées et contrôle de l'exécution des tests

`cargo test` offre plusieurs options pour affiner son comportement. Ces options sont passées après `--` si elles sont destinées au binaire de test lui-même, ou directement à `cargo test` si elles concernent Cargo.

Exécuter les tests en un seul thread : Par défaut, les tests s'exécutent en parallèle. Si vos tests ont des effets de bord qui interfèrent les uns avec les autres (ce qui est généralement une mauvaise pratique pour les tests unitaires) ou si vous voulez un débogage plus simple, vous pouvez les forcer à s'exécuter séquentiellement :

cargo test -- --test-threads=1

Afficher la sortie standard (`stdout`) des tests réussis : Par défaut, la sortie `println!` (ou autre sortie vers `stdout`) des tests n'est affichée que pour les tests qui échouent. Pour voir la sortie de tous les tests, y compris ceux qui réussissent (utile pour le débogage) :

cargo test -- --nocapture

Alternativement, la commande `cargo test --show-output` est une manière plus récente et recommandée d'obtenir le même résultat pour les versions récentes de Rust.

Exécuter les tests ignorés : Les tests annotés avec `#[ignore]` ne sont pas exécutés par défaut. Pour les exécuter spécifiquement :

cargo test -- --ignored

Pour exécuter tous les tests, y compris ceux ignorés :

cargo test -- --include-ignored

Spécifier le paquet ou la cible à tester : Dans un workspace Cargo avec plusieurs paquets, ou si vous voulez tester une cible spécifique (bibliothèque, binaire, exemple) :

cargo test -p nom_du_paquet  # Tester un paquet spécifique
cargo test --lib              # Tester uniquement la bibliothèque du paquet courant
cargo test --bin nom_du_binaire # Tester un binaire spécifique

Compiler en mode release pour les tests : Parfois, il est utile d'exécuter les tests avec les optimisations du mode release, par exemple pour des tests de performance ou pour vérifier des comportements qui diffèrent entre les modes debug et release (comme le dépassement d'entier).

cargo test --release

Cette commande prendra plus de temps à compiler mais les tests s'exécuteront plus rapidement et dans des conditions plus proches de la production.

Maîtriser `cargo test` et ses options vous permet d'intégrer efficacement les tests dans votre flux de travail Rust, que ce soit pour un développement itératif rapide ou pour des vérifications exhaustives avant un déploiement. C'est un outil indispensable pour tout développeur Rust soucieux de la qualité de son code.