--- description: globs: *.php alwaysApply: false --- # Tests de Mangarr ## Structure des Tests L'application suit une organisation stricte des tests reflétant l'architecture hexagonale : ``` tests/ ├── Domain/ # Tests unitaires par domaine │ ├── Manga/ │ │ ├── Application/ # Tests des cas d'utilisation │ │ ├── Domain/ # Tests du cœur métier │ │ └── Adapter/ # Implémentations InMemory des ports │ ├── Reader/ │ │ └── ... │ └── Scraping/ │ └── ... ├── Feature/ # Tests fonctionnels par domaine │ ├── Manga/ │ ├── Reader/ │ └── Scraping/ ├── Shared/ # Tests et adapters partagés │ └── Adapter/ # Adapters partagés entre domaines └── Fixtures/ # Fixtures de test partagées ``` ## Règles de Test ### 1. Tests Unitaires (Domain) - Localisation : `tests/Domain/NomDuDomain/` - Principes : - Tester chaque composant de manière isolée - Éviter l'utilisation de mocks - Utiliser des adapters InMemory - Nommer les classes de test avec le suffixe `Test` ### 2. Adapters de Test - Localisation : `tests/Domain/NomDuDomain/Adapter/` - Principes : - Implémenter les interfaces du domaine - Stocker les données dans des tableaux - Préfixer les classes avec `InMemory` - Si utilisé par plusieurs domaines → déplacer dans `tests/Shared/Adapter/` ### 3. Tests Fonctionnels (Feature) - Localisation : `tests/Feature/NomDuDomain/` - Principes : - Tester les endpoints HTTP - Utiliser le trait `ResetDatabase` - Tester le flux complet - Nommer les classes avec le suffixe `Test` ## Exemples de Code ### 1. Adapter InMemory ```php namespace Tests\Domain\Manga\Adapter; use App\Domain\Manga\Domain\Entity\Manga; use App\Domain\Manga\Domain\Repository\MangaRepositoryInterface; class InMemoryMangaRepository implements MangaRepositoryInterface { /** @var array */ private array $mangas = []; public function save(Manga $manga): void { $this->mangas[$manga->getId()->toString()] = $manga; } public function get(string $id): ?Manga { return $this->mangas[$id] ?? null; } public function clear(): void { $this->mangas = []; } } ``` ### 2. Test Unitaire ```php namespace Tests\Domain\Manga\Application; use App\Domain\Manga\Application\Command\CreateMangaCommand; use App\Domain\Manga\Application\CommandHandler\CreateMangaCommandHandler; use Tests\Domain\Manga\Adapter\InMemoryMangaRepository; use PHPUnit\Framework\TestCase; class CreateMangaCommandHandlerTest extends TestCase { private InMemoryMangaRepository $repository; private CreateMangaCommandHandler $handler; protected function setUp(): void { $this->repository = new InMemoryMangaRepository(); $this->handler = new CreateMangaCommandHandler($this->repository); } public function test_it_creates_manga(): void { // Given $command = new CreateMangaCommand('One Piece'); // When $this->handler->__invoke($command); // Then $mangas = $this->repository->findAll(); $this->assertCount(1, $mangas); $this->assertEquals('One Piece', $mangas[0]->getTitle()->value()); } } ``` ### 3. Test Fonctionnel ```php namespace Tests\Feature\Manga; use Tests\Shared\ResetDatabase; use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; class CreateMangaTest extends WebTestCase { use ResetDatabase; public function test_it_creates_manga_through_api(): void { // Given $client = static::createClient(); $data = ['title' => 'One Piece']; // When $client->request('POST', '/api/mangas', [], [], [], json_encode($data)); // Then $this->assertResponseIsSuccessful(); $this->assertJsonContains(['title' => 'One Piece']); } } ``` ### 4. Adapter Partagé ```php namespace Tests\Shared\Adapter; use App\Domain\Shared\Contract\MessageBusInterface; class InMemoryMessageBus implements MessageBusInterface { /** @var array */ private array $messages = []; public function dispatch(object $message): void { $this->messages[] = $message; } public function getDispatchedMessages(): array { return $this->messages; } public function clear(): void { $this->messages = []; } } ``` ## Bonnes Pratiques ### 1. Organisation des Tests - Un test par classe - Regrouper les tests par fonctionnalité - Suivre la même structure que le code source - Utiliser des données de test explicites ### 2. Nommage - Classes de test : `{ClassTestée}Test` - Méthodes de test : `test_it_{comportement_testé}` - Adapters : `InMemory{Interface}` ### 3. Assertions - Utiliser des assertions spécifiques - Vérifier les états plutôt que les interactions - Tester les cas d'erreur - Tester les cas limites ### 4. Données de Test - Utiliser des fixtures pour les données complexes - Créer des données spécifiques au test quand possible - Éviter les dépendances entre tests - Nettoyer l'état après chaque test