Stratégies de Test
Comprendre quand utiliser chaque type de test et comment construire une suite de tests efficace pour l’architecture hexagonale.
La pyramide de tests guide vos décisions : beaucoup de tests unitaires rapides, quelques tests d’intégration, et peu de tests E2E.
La Pyramide de Tests
📊 Pyramide
La Pyramide Classique
/\
/ \ E2E Tests (5%)
/----\ - Lents (secondes/minutes)
/ Inte \ - Fragiles
/ gration\ - Coûteux à maintenir
/------------\
/ \ Integration Tests (15%)
/ Unit Tests \ - Moyennement rapides
/------------------\- Tests avec dépendances
/____________________\
Unit Tests (80%)
- Ultra rapides (ms)
- Stables
- Faciles à maintenirPrincipe de base : Plus on monte dans la pyramide, plus les tests sont :
- ❌ Lents à exécuter
- ❌ Fragiles (cassent facilement)
- ❌ Coûteux à maintenir
- ✅ Réalistes (proches de la prod)
Stratégies par Couche
Domain
Tests du Domain Layer
Le domain est 100% testable en unitaire car il n’a AUCUNE dépendance externe.
Que Tester ?
Valider les règles métier
@Test
void trendResultShouldRejectNegativeValues() {
assertThrows(IllegalArgumentException.class,
() -> new TrendResult("java", -10, "2025-01-15"));
}Tester les Value Objects
@Test
void keywordShouldNormalizeInput() {
var keyword = new Keyword(" JAVA ");
assertEquals("java", keyword.value());
}Vérifier l’immutabilité
@Test
void trendResultShouldBeImmutable() {
var result = new TrendResult("java", 85, "2025-01-15");
// Impossible de modifier les valeurs
}Stratégie Recommandée
- ✅ 100% de couverture sur le domain
- ✅ Tests unitaires purs (pas de mocks)
- ✅ AAA pattern (Arrange/Act/Assert)
- ✅ Tests rapides (ms)
Stratégie Complète pour un Use Case
Voici comment tester un use case complet en suivant la pyramide.
Exemple : GetTrendsUseCase
Tests Unitaires du Domain (80%)
// Tester TrendResult, TrendRequest, règles métier
class TrendResultTest {
@Test void shouldCreateValidTrendResult() { }
@Test void shouldRejectNegativeValue() { }
@Test void shouldFormatDateCorrectly() { }
}Objectif : Valider TOUTE la logique métier.
Tests Unitaires de l’Application (80%)
// Tester GetTrendsUseCase avec mocks
class GetTrendsUseCaseTest {
@Mock TrendRepository mockRepository;
@Test void shouldReturnTrends() { }
@Test void shouldHandleErrors() { }
@Test void shouldValidateInput() { }
}Objectif : Tester l’orchestration sans dépendances.
Tests d’Intégration (15%)
// Tester le flux complet HTTP → Domain
@MicronautTest
class TrendControllerIntegrationTest {
@Inject HttpClient client;
@Test void shouldReturnTrendsViaHTTP() {
var response = client.toBlocking()
.exchange("/api/trends?keyword=java", TrendDto[].class);
assertEquals(HttpStatus.OK, response.status());
}
}Objectif : Valider l’intégration complète.
Tests d’Architecture (Bonus)
// Vérifier que l'architecture est respectée
@Test
void domainShouldNotDependOnInfrastructure() {
noClasses().that().resideInPackage("..domain..")
.should().dependOnClassesThat()
.resideInPackage("..infrastructure..")
.check(classes);
}Objectif : Garantir le respect de l’architecture.
Matrice de Décision
Utilisez ce tableau pour décider quel type de test écrire.
| Vous Voulez Tester | Type de Test | Outil | Temps |
|---|---|---|---|
| Règle métier pure | ✅ Unitaire | JUnit | 1ms |
| Validation domain | ✅ Unitaire | JUnit | 1ms |
| Use case (orchestration) | ✅ Unitaire | Mockito | 5ms |
| Controller → Use Case | ⚠️ Intégration | @MicronautTest | 100ms |
| Appel HTTP externe | ⚠️ Intégration | @MicronautTest | 200ms |
| Conversion JSON ↔ DTO | ⚠️ Intégration | ObjectMapper | 50ms |
| Architecture respectée | ✅ Architecture | ArchUnit | 10ms |
| Parcours utilisateur | ❌ E2E | Selenium | 10s |
Anti-Patterns à Éviter
❌ Trop de Mocks
Anti-Pattern : Trop de Mocks
Problème : Mocker dans les tests unitaires du domain.
// ❌ MAUVAIS : Le domain n'a pas de dépendances !
class TrendResultTest {
@Mock SomeService mockService; // ❌ Pourquoi ?
@Test
void test() {
// Le domain est pur, pas besoin de mocks
}
}Solution :
// ✅ BON : Tests purs sans mocks
class TrendResultTest {
@Test
void shouldCreateTrendResult() {
var result = new TrendResult("java", 85, "2025-01-15");
assertEquals("java", result.keyword());
}
}Règle : Si vous mockez dans le domain, c’est que votre domain a des dépendances → architecture incorrecte !
Checklist : Quelle Stratégie pour Votre Projet ?
Commencez par les tests d’architecture
# Installation ArchUnit
mvn archunit:checkPourquoi en premier ? Garantit que l’architecture est respectée dès le début.
Écrivez les tests unitaires du domain
- ✅ Testez toutes les règles métier
- ✅ Validez les Value Objects
- ✅ Vérifiez les invariants
Objectif : 100% de couverture du domain.
Ajoutez les tests unitaires des use cases
- ✅ Mocker les ports (repositories, services)
- ✅ Tester l’orchestration
- ✅ Vérifier la gestion d’erreurs
Objectif : 80%+ de couverture de l’application layer.
Complétez avec des tests d’intégration
- ⚠️ Tester les adapters (API, BDD)
- ⚠️ Valider le flux HTTP complet
- ⚠️ Vérifier la serialisation JSON
Objectif : Cas critiques uniquement (15% du total).
Automatisez dans la CI/CD
# GitHub Actions
- name: Run tests
run: mvn testObjectif : Tests exécutés à chaque commit.
Récapitulatif
Une bonne stratégie de test = 80% unitaires, 15% intégration, 5% E2E + tests d’architecture.
Règles d’Or
- Domain → 100% tests unitaires purs
- Application → Tests unitaires avec mocks
- Infrastructure → Tests d’intégration ciblés
- Architecture → ArchUnit pour validation automatique
Temps d’Exécution Cible
- ✅ Tests unitaires : < 1 seconde pour toute la suite
- ⚠️ Tests d’intégration : < 30 secondes
- ✅ Tests d’architecture : < 1 seconde
- 🎯 Total : < 1 minute pour 200+ tests
Prochaines Étapes
Vous avez maintenant une stratégie complète. Passez à la pratique !
- 📖 Tests Unitaires - Écrire des tests unitaires avec Mockito
- 📖 Tests d’Intégration - Tester avec
@MicronautTest - 📖 Tests d’Architecture - Garantir l’architecture avec ArchUnit
- 📖 Tests Manuels - Valider l’API avec Postman/cURL