Introduction : Pourquoi les tests en Rust ne sont pas seulement à la mode, mais nécessaires
\n\nRust est un langage réputé pour sa sécurité mémoire et ses hautes performances. Cependant, même le compilateur 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 système de types et de propriété élimine déjà toute une classe d'erreurs à la compilation, mais les tests unitaires, d'intégration et de bout en bout (e2e) permettent de s'assurer que votre code fait exactement ce que vous avez prévu.
\n\nDans cet article, nous allons aborder trois niveaux de tests en Rust : des tests unitaires isolés aux scénarios e2e complets. Vous apprendrez à écrire des tests efficaces, quels outils utiliser et comment organiser l'infrastructure de test. Ce contenu sera utile aussi bien aux débutants qui commencent tout juste à apprendre Rust qu'aux développeurs expérimentés souhaitant systématiser leurs connaissances.
\n\n1. Tests unitaires : le fondement de base
\n\nLes tests unitaires sont le niveau de vérification le plus rapide et le plus isolé. Ils testent des fonctions individuelles, des méthodes ou de petits modules indépendamment des dépendances externes. En Rust, les tests unitaires sont généralement placés dans le même fichier que le code testé, à l'intérieur du module tests.
1.1. Structure et attributs
\n\nPour créer un test unitaire, on utilise l'attribut #[test]. La fonction ainsi marquée ne prend pas d'arguments et ne retourne rien (ou retourne un Result). Si une panique (panic) se produit à l'intérieur de la fonction, le test est considéré comme échoué.
// src/lib.rs\npub fn add(a: i32, b: i32) -> i32 {\n a + b\n}\n\n#[cfg(test)]\nmod tests {\n use super::*;\n\n #[test]\n fn test_add() {\n assert_eq!(add(2, 2), 4);\n }\n\n #[test]\n fn test_add_negative() {\n assert_eq!(add(-1, -1), -2);\n }\n}\n\nNotez l'utilisation de #[cfg(test)]. Cet attribut garantit que le module de test ne sera compilé que lors de l'exécution des tests (cargo test). Il ne sera pas inclus dans la version de production.
1.2. Macros pour les vérifications
\n\nRust fournit plusieurs macros pour vérifier les assertions :
\n\n- \n
assert!(expr)— vérifie que l'expression est vraie. \n assert_eq!(left, right)— vérifie l'égalité (nécessitePartialEqetDebug). \n assert_ne!(left, right)— vérifie l'inégalité. \n panic!("message")— provoque une panique de force (utilisé pour tester les erreurs). \n
1.3. Tester les paniques avec should_panic
\n\nSi votre fonction doit paniquer en cas de données d'entrée incorrectes, utilisez l'attribut #[should_panic] :
pub fn divide(a: f64, b: f64) -> f64 {\n if b == 0.0 {\n panic!("Division by zero");\n }\n a / b\n}\n\n#[cfg(test)]\nmod tests {\n use super::*;\n\n #[test]\n #[should_panic(expected = "Division by zero")]\n fn test_divide_by_zero() {\n divide(10.0, 0.0);\n }\n}\n\nLe paramètre expected n'est pas obligatoire, mais il rend le test plus précis : la panique doit contenir la chaîne spécifiée.
2. Tests d'intégration : vérification de l'interaction des modules
\n\nLes tests d'intégration vérifient comment les différentes parties de votre application fonctionnent ensemble. Contrairement aux tests unitaires, ils se trouvent dans un répertoire séparé tests/ à la racine du projet (à côté de src/). Chaque fichier de ce dossier est compilé comme une crate bibliothèque distincte.
2.1. Création d'un test d'intégration
\n\nCréez un fichier tests/integration_test.rs :
// tests/integration_test.rs\nuse my_crate::add; // suppose que votre crate s'appelle my_crate\n\n#[test]\nfn test_add_integration() {\n let result = add(5, 10);\n assert_eq!(result, 15);\n}\n\nPour exécuter uniquement les tests d'intégration