Introduction : Pourquoi les tests en Rust sont simples et fiables
Rust a été conçu dès le départ comme un langage qui aide à écrire du code sûr et fiable. Cependant, même le système de types le plus strict ne garantit pas l'exactitude de la logique métier. C'est là que les tests entrent en jeu. En Rust, le processus d'écriture de tests est intégré directement dans l'outillage cargo, ce qui le rend incroyablement pratique et accessible.
Dans cet article, nous allons aborder trois niveaux de tests : unit (tests unitaires), integration (tests d'intégration) et e2e (tests de bout en bout). Vous apprendrez à écrire des tests, quand utiliser chaque approche et comment automatiser la vérification de votre code. Ce contenu sera utile aussi bien aux débutants qu'aux développeurs expérimentés qui passent à Rust.
Tests unitaires : Tester de l'intérieur
Les tests unitaires sont le type de test le plus basique et le plus courant. Ils vérifient des fonctions, méthodes ou modules individuels isolés du reste du système. En Rust, il est courant d'écrire les tests unitaires directement dans le même fichier que le code testé, ou dans un fichier séparé tests.rs à l'intérieur du module.
Comment écrire des tests unitaires en Rust
Pour créer un test, on utilise l'attribut #[test]. Les tests sont placés dans un module avec l'attribut #[cfg(test)], qui garantit que le code de test ne sera pas inclus dans la compilation finale.
// src/lib.rspub fn add(a: i32, b: i32) -> i32 { a + b}
pub fn divide(a: f64, b: f64) -> Result<f64, String> { if b == 0.0 { Err(String::from("Division by zero")) } else { Ok(a / b) }}
#[cfg(test)]mod tests { use super::*;
#[test] fn test_add() { assert_eq!(add(2, 3), 5); }
#[test] fn test_add_negative() { assert_eq!(add(-1, 1), 0); }
#[test] fn test_divide_success() { let result = divide(10.0, 2.0); assert!(result.is_ok()); assert_eq!(result.unwrap(), 5.0); }
#[test] fn test_divide_by_zero() { let result = divide(10.0, 0.0); assert!(result.is_err()); assert_eq!(result.unwrap_err(), "Division by zero"); }}Exécution des tests unitaires
Vous pouvez lancer les tests avec une seule commande :
cargo testLa sortie indiquera combien de tests ont réussi et combien ont échoué. Pour exécuter un test spécifique, utilisez un filtre :
cargo test test_addLes tests unitaires sont idéaux pour vérifier les cas limites, la gestion des erreurs et l'exactitude des algorithmes. Ils sont rapides et ne nécessitent pas de dépendances externes.
Tests d'intégration : Vérifier l'interaction des modules
Les tests d'intégration vérifient comment différents modules de votre bibliothèque ou application fonctionnent ensemble. Contrairement aux tests unitaires, ils n'ont pas accès aux fonctions internes (sauf si celles-ci sont exportées avec pub). Les tests d'intégration sont écrits dans un dossier séparé tests/ à la racine du projet.
Structure des tests d'intégration
Créez un dossier tests/ à la racine de votre projet et ajoutez-y des fichiers de test. Chaque fichier sera compilé comme une crate distincte.
// tests/integration_test.rsuse my_crate; // nom de votre crate
#[test]fn test_add_integration() { assert_eq!(my_crate::add(10, 20), 30);}
#[test]fn test_divide_integration() { let result = my_crate::divide(100.0, 4.0); assert_eq!(result.unwrap(), 25.0);}Module commun pour les tests
Si plusieurs tests d'intégration utilisent les mêmes fonctions auxiliaires, placez-les dans un module commun. Pour cela, créez un dossier tests/common/mod.rs :
// tests/common/mod.rspub fn setup() { // initialisation du logger, de la base de données, etc. println!("Setting up test environment...");}
// tests/another_test.rsmod common;
#[test]fn test