202 lines
5.1 KiB
Plaintext
202 lines
5.1 KiB
Plaintext
---
|
|
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 |