8.2 KiB
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
cargo test
Exécuter un fichier de tests spécifique
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
cargo test test_good_pronounceable_words
Exécuter avec sortie détaillée
cargo test -- --nocapture
Exécuter avec affichage des tests ignorés
cargo test -- --ignored
Voir la couverture de test (avec tarpaulin)
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.
let rng = StdRng::seed_from_u64(42); // Reproductible
2. Tests descriptifs
Les noms de tests décrivent clairement ce qui est testé :
#[test]
fn test_generation_produces_unique_anagrams() { ... }
#[test]
fn test_common_consonant_clusters_not_penalized() { ... }
3. Arrange-Act-Assert
Structure claire des tests :
#[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 :
#[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 :
#[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 :
#[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 :
#[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 :
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 :
-
É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
-
Couvrir les cas nominaux et d'erreur
- Cas nominal (happy path)
- Cas limites (edge cases)
- Cas d'erreur
-
Maintenir l'isolation
- Pas de dépendances entre tests
- Pas d'état partagé
-
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 :
# .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
cargo test -- --nocapture --test-threads=1
Exécuter avec le debugger
rust-gdb --args target/debug/deps/analyzer_tests-* test_name
Logs de débogage
Utiliser dbg!() temporairement dans les tests :
#[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/ :
// 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);