---
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<string, Manga> */
    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<object> */
    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 