312 lines
6.8 KiB
Markdown
312 lines
6.8 KiB
Markdown
# Framework Make Story
|
|
|
|
Génère une Story Foundry pour fixtures de tests.
|
|
|
|
## Vue d'ensemble
|
|
Cette skill crée une Story Zenstruck Foundry pour orchestrer la création de fixtures de tests cohérentes et réutilisables.
|
|
|
|
## Caractéristiques
|
|
|
|
### Classe Story générée
|
|
- Extends Story
|
|
- Implements StoryInterface
|
|
- Classe `final`
|
|
- Méthode `build()` avec scénarios
|
|
- Utilise Factories pour créer instances
|
|
- Scénarios prédéfinis
|
|
|
|
### Classe AppStory
|
|
- Point d'entrée global
|
|
- Attribut #[AsFixture(name: 'main')]
|
|
- Charge toutes les stories
|
|
- Gère les dépendances entre stories
|
|
|
|
## Utilisation
|
|
|
|
```bash
|
|
Use skill framework:make:story
|
|
```
|
|
|
|
Vous serez invité à fournir le nom de l'entité.
|
|
|
|
## Exemple d'utilisation
|
|
|
|
```bash
|
|
EntityName: Product
|
|
```
|
|
|
|
Génère :
|
|
```php
|
|
// src/Story/ProductStory.php
|
|
final class ProductStory extends Story implements StoryInterface
|
|
{
|
|
public function build(): void
|
|
{
|
|
// Produit par défaut
|
|
ProductFactory::createOne();
|
|
|
|
// Produits avec IDs spécifiques
|
|
ProductFactory::new()
|
|
->withSpecificId('01234567-89ab-cdef-0123-456789abcdef')
|
|
->create();
|
|
|
|
// Plusieurs produits
|
|
ProductFactory::createMany(10);
|
|
}
|
|
}
|
|
|
|
// src/Story/AppStory.php (updated)
|
|
#[AsFixture(name: 'main')]
|
|
final class AppStory extends Story implements StoryInterface
|
|
{
|
|
public function build(): void
|
|
{
|
|
ProductStory::load();
|
|
// ... autres stories
|
|
}
|
|
}
|
|
```
|
|
|
|
## Structure créée
|
|
|
|
```
|
|
src/
|
|
└── Story/
|
|
├── {EntityName}Story.php
|
|
└── AppStory.php
|
|
```
|
|
|
|
## Prérequis
|
|
- L'entité doit exister
|
|
- La Factory doit exister (créée automatiquement si absente)
|
|
- StoryInterface doit exister dans Contracts
|
|
- Zenstruck Foundry installé
|
|
|
|
## Usage dans les tests
|
|
|
|
### Tests unitaires
|
|
```php
|
|
use App\Story\ProductStory;
|
|
|
|
final class ProductServiceTest extends TestCase
|
|
{
|
|
protected function setUp(): void
|
|
{
|
|
ProductStory::load();
|
|
}
|
|
|
|
public function testCalculateTotalPrice(): void
|
|
{
|
|
$products = ProductFactory::repository()->findAll();
|
|
$service = new ProductService();
|
|
|
|
$total = $service->calculateTotal($products);
|
|
|
|
self::assertGreaterThan(0, $total);
|
|
}
|
|
}
|
|
```
|
|
|
|
### Tests fonctionnels
|
|
```php
|
|
use App\Story\AppStory;
|
|
|
|
final class ProductControllerTest extends WebTestCase
|
|
{
|
|
protected function setUp(): void
|
|
{
|
|
AppStory::load();
|
|
}
|
|
|
|
public function testListProducts(): void
|
|
{
|
|
$client = static::createClient();
|
|
$client->request('GET', '/products');
|
|
|
|
self::assertResponseIsSuccessful();
|
|
self::assertSelectorExists('.product-item');
|
|
}
|
|
}
|
|
```
|
|
|
|
### Fixtures Doctrine
|
|
```bash
|
|
# Charger toutes les fixtures
|
|
php bin/console doctrine:fixtures:load --append
|
|
|
|
# Ou juste AppStory via attribut #[AsFixture]
|
|
```
|
|
|
|
## Enrichissement (principe YAGNI)
|
|
|
|
### Scénarios métier
|
|
```php
|
|
final class ProductStory extends Story implements StoryInterface
|
|
{
|
|
public function build(): void
|
|
{
|
|
// Scénario : catalogue actif
|
|
ProductFactory::new()
|
|
->active()
|
|
->createMany(20);
|
|
|
|
// Scénario : produits en promotion
|
|
ProductFactory::new()
|
|
->active()
|
|
->createMany(5, ['price' => 9.99]);
|
|
|
|
// Scénario : rupture de stock
|
|
ProductFactory::new()
|
|
->outOfStock()
|
|
->createMany(3);
|
|
|
|
// Scénario : nouveau produit vedette
|
|
ProductFactory::createOne([
|
|
'name' => 'Featured Product',
|
|
'price' => 199.99,
|
|
'stock' => 100,
|
|
'isActive' => true,
|
|
]);
|
|
}
|
|
}
|
|
```
|
|
|
|
### Avec relations
|
|
```php
|
|
final class ProductStory extends Story implements StoryInterface
|
|
{
|
|
public function build(): void
|
|
{
|
|
// Charger dépendances
|
|
CategoryStory::load();
|
|
|
|
// Récupérer catégories
|
|
$electronics = CategoryFactory::find(['name' => 'Electronics']);
|
|
$books = CategoryFactory::find(['name' => 'Books']);
|
|
|
|
// Produits par catégorie
|
|
ProductFactory::new()
|
|
->inCategory($electronics)
|
|
->createMany(10);
|
|
|
|
ProductFactory::new()
|
|
->inCategory($books)
|
|
->createMany(15);
|
|
}
|
|
}
|
|
```
|
|
|
|
### États nommés
|
|
```php
|
|
final class ProductStory extends Story implements StoryInterface
|
|
{
|
|
public function build(): void
|
|
{
|
|
// États nommés pour réutilisation
|
|
$this->addState('premium', ProductFactory::createOne([
|
|
'name' => 'Premium Product',
|
|
'price' => 999.99,
|
|
]));
|
|
|
|
$this->addState('cheap', ProductFactory::createOne([
|
|
'name' => 'Cheap Product',
|
|
'price' => 9.99,
|
|
]));
|
|
|
|
$this->addState('test_product', ProductFactory::createOne([
|
|
'id' => Uuid::fromString('01234567-89ab-cdef-0123-456789abcdef'),
|
|
]));
|
|
}
|
|
}
|
|
|
|
// Usage
|
|
$premium = ProductStory::load()->get('premium');
|
|
$cheap = ProductStory::load()->get('cheap');
|
|
```
|
|
|
|
### Scénarios complexes
|
|
```php
|
|
final class OrderStory extends Story implements StoryInterface
|
|
{
|
|
public function build(): void
|
|
{
|
|
// Charger dépendances
|
|
UserStory::load();
|
|
ProductStory::load();
|
|
|
|
$users = UserFactory::repository()->findAll();
|
|
$products = ProductFactory::repository()->findAll();
|
|
|
|
// Commandes par utilisateur
|
|
foreach ($users as $user) {
|
|
OrderFactory::createOne([
|
|
'user' => $user,
|
|
'items' => array_slice($products, 0, rand(1, 5)),
|
|
]);
|
|
}
|
|
|
|
// Commande de test spécifique
|
|
$this->addState('test_order', OrderFactory::createOne([
|
|
'user' => UserStory::load()->get('test_user'),
|
|
'items' => [ProductStory::load()->get('test_product')],
|
|
'status' => 'pending',
|
|
]));
|
|
}
|
|
}
|
|
```
|
|
|
|
## AppStory orchestration
|
|
|
|
```php
|
|
#[AsFixture(name: 'main')]
|
|
final class AppStory extends Story implements StoryInterface
|
|
{
|
|
public function build(): void
|
|
{
|
|
// Ordre important : dépendances d'abord
|
|
CategoryStory::load();
|
|
ProductStory::load();
|
|
UserStory::load();
|
|
OrderStory::load();
|
|
ReviewStory::load();
|
|
}
|
|
}
|
|
```
|
|
|
|
## Configuration Foundry
|
|
|
|
```yaml
|
|
# config/packages/zenstruck_foundry.yaml
|
|
when@dev:
|
|
zenstruck_foundry:
|
|
auto_refresh_proxies: false
|
|
|
|
when@test:
|
|
zenstruck_foundry:
|
|
auto_refresh_proxies: false
|
|
make_factory:
|
|
default_namespace: 'App\Factory'
|
|
```
|
|
|
|
## Commandes utiles
|
|
|
|
```bash
|
|
# Charger fixtures
|
|
php bin/console doctrine:fixtures:load --append
|
|
|
|
# Purger DB puis charger
|
|
php bin/console doctrine:fixtures:load
|
|
|
|
# Dans tests
|
|
AppStory::load();
|
|
ProductStory::load();
|
|
```
|
|
|
|
## Principes Elegant Objects appliqués
|
|
- Classe finale
|
|
- Implements StoryInterface
|
|
- Méthode `build()` claire et concise
|
|
- Scénarios nommés explicitement
|
|
- DRY : fixtures centralisées
|
|
- États nommés pour réutilisation
|