# Guide de test - Anagram Generator ## Structure des tests Le projet utilise une architecture de tests modulaire avec les tests séparés du code source. Tous les tests sont situés dans le répertoire `tests/` à la racine du projet. ``` tests/ ├── analyzer_tests.rs # Tests pour PronounceabilityAnalyzer ├── generator_tests.rs # Tests pour AnagramGenerator ├── types_tests.rs # Tests pour Anagram et PronouncabilityScore └── integration_tests.rs # Tests d'intégration end-to-end ``` ## Types de tests ### Tests unitaires par module #### analyzer_tests.rs Tests du module `analyzer` qui vérifient : - Le scoring des mots prononçables - La classification des caractères (voyelles/consonnes) - La détection des clusters de consonnes communs - Les pénalités pour différents patterns phonétiques - Les bonus pour bonne alternance voyelle-consonne **Nombre de tests** : 11 #### generator_tests.rs Tests du module `generator` qui vérifient : - La génération de valides anagrammes - Le respect du score minimum - L'unicité des anagrammes générés - Le tri par score - L'exclusion du mot original - La normalisation de l'entrée - Le pattern builder de configuration **Nombre de tests** : 9 #### types_tests.rs Tests des types de domaine qui vérifient : - La création et manipulation de `PronouncabilityScore` - Les opérations saturantes (add/sub) - Le clamping des valeurs (0-100) - La création et comparaison d'`Anagram` - Le tri des anagrammes par score **Nombre de tests** : 10 ### Tests d'intégration #### integration_tests.rs Tests end-to-end qui vérifient : - Le flux complet de génération d'anagrammes - L'intégration entre l'analyseur et le générateur - La personnalisation du scorer via le trait - La configuration avancée - Les cas d'usage réels **Nombre de tests** : 6 ## Exécution des tests ### Exécuter tous les tests ```bash cargo test ``` ### Exécuter un fichier de tests spécifique ```bash cargo test --test analyzer_tests cargo test --test generator_tests cargo test --test types_tests cargo test --test integration_tests ``` ### Exécuter un test particulier ```bash cargo test test_good_pronounceable_words ``` ### Exécuter avec sortie détaillée ```bash cargo test -- --nocapture ``` ### Exécuter avec affichage des tests ignorés ```bash cargo test -- --ignored ``` ### Voir la couverture de test (avec tarpaulin) ```bash cargo install cargo-tarpaulin cargo tarpaulin --out Html ``` ## Métriques de tests - **Total de tests** : 36 tests - Tests d'analyse : 11 - Tests de génération : 9 - Tests de types : 10 - Tests d'intégration : 6 - **Couverture estimée** : ~90% du code - **Temps d'exécution** : < 2 secondes pour tous les tests ## Principes de test appliqués ### 1. Tests indépendants Chaque test est autonome et n'affecte pas les autres. Utilisation de seeds fixes pour les générateurs aléatoires quand nécessaire. ```rust let rng = StdRng::seed_from_u64(42); // Reproductible ``` ### 2. Tests descriptifs Les noms de tests décrivent clairement ce qui est testé : ```rust #[test] fn test_generation_produces_unique_anagrams() { ... } #[test] fn test_common_consonant_clusters_not_penalized() { ... } ``` ### 3. Arrange-Act-Assert Structure claire des tests : ```rust #[test] fn test_score_saturating_operations() { // Arrange let score = PronouncabilityScore::new(80); // Act let increased = score.saturating_add(30); // Assert assert_eq!(increased.value(), 100); } ``` ### 4. Tests du comportement, pas de l'implémentation Focus sur le comportement observable plutôt que les détails d'implémentation. ### 5. Tests de cas limites Couverture des cas limites et d'erreur : ```rust #[test] fn test_empty_string_scores_zero() { ... } #[test] fn test_score_value_clamped_to_range() { ... } #[test] fn test_generation_with_repeated_letters() { ... } ``` ## Écrire de nouveaux tests ### Pour ajouter un test d'analyse Créer un nouveau test dans [tests/analyzer_tests.rs](tests/analyzer_tests.rs) : ```rust #[test] fn test_new_phonetic_pattern() { let analyzer = PronounceabilityAnalyzer::with_defaults(); // Test du comportement attendu assert!(analyzer.score("example").value() > expected_threshold); } ``` ### Pour ajouter un test de génération Créer un nouveau test dans [tests/generator_tests.rs](tests/generator_tests.rs) : ```rust #[test] fn test_new_generation_behavior() { let rng = StdRng::seed_from_u64(42); let scorer = PronounceabilityAnalyzer::with_defaults(); let mut generator = AnagramGenerator::new(rng, scorer); let config = GenerationConfig::new(min_score, max_attempts); let anagrams = generator.generate("test", count, &config); // Vérifications assert!(!anagrams.is_empty()); } ``` ### Pour ajouter un test d'intégration Créer un nouveau test dans [tests/integration_tests.rs](tests/integration_tests.rs) : ```rust #[test] fn test_end_to_end_new_feature() { // Setup complet du système let rng = thread_rng(); let scorer = PronounceabilityAnalyzer::with_defaults(); let mut generator = AnagramGenerator::new(rng, scorer); // Test du flux complet let config = GenerationConfig::new(50, 1000); let result = generator.generate("word", 5, &config); // Vérifications end-to-end assert!(result.meets_requirements()); } ``` ## Tests avec mocks et stubs Pour tester l'injection de dépendances avec des scorers personnalisés : ```rust struct MockScorer { fixed_score: u32, } impl PronounceabilityScorer for MockScorer { fn score(&self, _text: &str) -> PronouncabilityScore { PronouncabilityScore::new(self.fixed_score) } } #[test] fn test_with_mock_scorer() { let rng = thread_rng(); let scorer = MockScorer { fixed_score: 100 }; let mut generator = AnagramGenerator::new(rng, scorer); // Tous les anagrammes auront un score de 100 let config = GenerationConfig::new(99, 100); let anagrams = generator.generate("test", 5, &config); assert!(anagrams.len() > 0); for anagram in anagrams { assert_eq!(anagram.score().value(), 100); } } ``` ## Stratégie de test pour les contributions Lors de l'ajout de nouvelles fonctionnalités : 1. **Écrire les tests d'abord** (TDD) - Définir le comportement attendu - Écrire les tests qui échouent - Implémenter la fonctionnalité - Vérifier que les tests passent 2. **Couvrir les cas nominaux et d'erreur** - Cas nominal (happy path) - Cas limites (edge cases) - Cas d'erreur 3. **Maintenir l'isolation** - Pas de dépendances entre tests - Pas d'état partagé 4. **Tests rapides** - Éviter les I/O inutiles - Utiliser des seeds fixes pour le random - Pas de sleep() ou d'attente ## Continuous Integration Le projet est prêt pour CI/CD. Configuration recommandée : ```yaml # .github/workflows/test.yml name: Tests on: [push, pull_request] jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - uses: actions-rs/toolchain@v1 with: toolchain: stable - run: cargo test --all-features - run: cargo test --doc ``` ## Debugging des tests ### Afficher la sortie des tests ```bash cargo test -- --nocapture --test-threads=1 ``` ### Exécuter avec le debugger ```bash rust-gdb --args target/debug/deps/analyzer_tests-* test_name ``` ### Logs de débogage Utiliser `dbg!()` temporairement dans les tests : ```rust #[test] fn test_debug() { let score = analyzer.score("test"); dbg!(score); // Affiche la valeur assert!(score.value() > 50); } ``` ## Benchmark (optionnel) Pour des benchmarks de performance, créer `benches/` : ```rust // benches/generation_benchmark.rs use criterion::{black_box, criterion_group, criterion_main, Criterion}; fn benchmark_generation(c: &mut Criterion) { c.bench_function("generate 10 anagrams", |b| { b.iter(|| { // Code à benchmarker }); }); } criterion_group!(benches, benchmark_generation); criterion_main!(benches); ``` ## Ressources - [The Rust Book - Testing](https://doc.rust-lang.org/book/ch11-00-testing.html) - [Cargo Test Documentation](https://doc.rust-lang.org/cargo/commands/cargo-test.html) - [Integration Testing in Rust](https://doc.rust-lang.org/rust-by-example/testing/integration_testing.html)