Claude Code Plugins

Community-maintained marketplace

Feedback

framework:make:story

@atournayre/claude-marketplace
4
0

Génère Story Foundry pour fixtures de tests

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name framework:make:story
description Génère Story Foundry pour fixtures de tests
license MIT
version 1.0.0

Framework Make Story Skill

Description

Génère une Story Foundry pour créer des fixtures de tests complexes avec des scénarios prédéfinis.

La Story orchestre la création de multiples instances d'entités via les Factories et permet de charger des jeux de données cohérents pour les tests.

Usage

Use skill framework:make:story

Vous serez invité à fournir :
- Le nom de l'entité (ex: Product, User, Order)

Templates

  • Story/UtilisateurStory.php - Template de story pour une entité
  • Story/AppStory.php - Template de story globale (créé si absent)

Variables requises

  • {EntityName} - Nom de l'entité en PascalCase (ex: Utilisateur, Product)
  • {entityName} - Nom de l'entité en camelCase (ex: utilisateur, product)
  • {namespace} - Namespace du projet (défaut: App)

Dépendances

  • Requiert que l'entité existe dans src/Entity/{EntityName}.php
  • Appelle automatiquement framework:make:factory si la Factory n'existe pas
  • Requiert que les Contracts existent (StoryInterface)

Outputs

  • src/Story/{EntityName}Story.php
  • src/Story/AppStory.php (si n'existe pas déjà)

Workflow

  1. Demander le nom de l'entité (EntityName)
  2. Vérifier que l'entité existe dans src/Entity/{EntityName}.php
    • Si non : arrêter et demander de créer l'entité d'abord
  3. Vérifier que la Factory existe dans src/Factory/{EntityName}Factory.php
    • Si non : appeler framework:make:factory
  4. Générer la Story depuis le template :
    • Remplacer {EntityName} par le nom fourni
    • Remplacer {entityName} par la version camelCase
  5. Vérifier si src/Story/AppStory.php existe
    • Si non : créer AppStory avec le template
    • Si oui : ajouter {EntityName}Story::load(); dans la méthode build()
  6. Afficher les fichiers créés

Patterns appliqués

Classe Story

  • Extends Story
  • Implements StoryInterface
  • Classe final
  • Méthode build() créant les fixtures
  • Utilise les Factories pour créer les instances
  • Scénarios de tests prédéfinis

Classe AppStory

  • Extends Story
  • Implements StoryInterface
  • Classe final
  • Attribut #[AsFixture(name: 'main')]
  • Méthode build() chargeant toutes les stories
  • Point d'entrée unique pour charger toutes les fixtures

Exemple

Use skill framework:make:story

# Saisies utilisateur :
EntityName: Product

# Résultat :
✓ src/Story/ProductStory.php
✓ src/Story/AppStory.php (updated)

Fichiers générés :

// src/Story/ProductStory.php
<?php

declare(strict_types=1);

namespace App\Story;

use App\Contracts\Story\StoryInterface;
use App\Factory\ProductFactory;
use Zenstruck\Foundry\Story;

final class ProductStory extends Story implements StoryInterface
{
    public function build(): void
    {
        // Produit par défaut
        ProductFactory::createOne();

        // Produits avec IDs spécifiques pour les tests
        ProductFactory::new()
            ->withSpecificId('01234567-89ab-cdef-0123-456789abcdef')
            ->create();

        // Créer plusieurs produits
        ProductFactory::createMany(10);
    }
}

// src/Story/AppStory.php
<?php

namespace App\Story;

use App\Contracts\Story\StoryInterface;
use Zenstruck\Foundry\Attribute\AsFixture;
use Zenstruck\Foundry\Story;

#[AsFixture(name: 'main')]
final class AppStory extends Story implements StoryInterface
{
    public function build(): void
    {
        ProductStory::load();
    }
}

Usage dans les tests

Charger la story

use App\Story\ProductStory;

final class ProductTest extends TestCase
{
    protected function setUp(): void
    {
        ProductStory::load();
    }

    public function testProductsAreLoaded(): void
    {
        $products = ProductFactory::repository()->findAll();

        self::assertCount(12, $products); // 1 défaut + 1 spécifique + 10 random
    }
}

Charger toutes les stories

use App\Story\AppStory;

final class IntegrationTest extends TestCase
{
    protected function setUp(): void
    {
        AppStory::load(); // Charge ProductStory + autres
    }
}

Fixture Doctrine

# Dans config/packages/zenstruck_foundry.yaml
when@dev:
    zenstruck_foundry:
        auto_refresh_proxies: false

# Charger les fixtures
php bin/console doctrine:fixtures:load --append

Enrichissement de la Story

Scénarios complexes

final class ProductStory extends Story implements StoryInterface
{
    public function build(): void
    {
        // Produits actifs
        ProductFactory::new()
            ->active()
            ->createMany(5);

        // Produits inactifs
        ProductFactory::new()
            ->inactive()
            ->createMany(3);

        // Produits en rupture
        ProductFactory::new()
            ->outOfStock()
            ->createMany(2);

        // Produits premium
        ProductFactory::new()
            ->expensive()
            ->createMany(3);

        // Produit spécifique pour tests
        ProductFactory::createOne([
            'name' => 'Test Product',
            'price' => 99.99,
            'stock' => 10,
        ]);
    }
}

Avec relations

final class ProductStory extends Story implements StoryInterface
{
    public function build(): void
    {
        // Créer catégories d'abord
        CategoryStory::load();

        $electronics = CategoryFactory::find(['name' => 'Electronics']);
        $books = CategoryFactory::find(['name' => 'Books']);

        // Produits électroniques
        ProductFactory::new()
            ->inCategory($electronics)
            ->createMany(5);

        // Livres
        ProductFactory::new()
            ->inCategory($books)
            ->createMany(10);
    }
}

Avec états nommés

final class ProductStory extends Story implements StoryInterface
{
    public function build(): void
    {
        // États nommés pour réutilisation dans tests
        $this->addState('premium_product', ProductFactory::createOne([
            'name' => 'Premium Product',
            'price' => 999.99,
        ]));

        $this->addState('cheap_product', ProductFactory::createOne([
            'name' => 'Cheap Product',
            'price' => 9.99,
        ]));
    }
}

// Usage dans test
$premium = ProductStory::load()->get('premium_product');

AppStory orchestration

#[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();
    }
}

Notes

  • AppStory est le point d'entrée pour charger toutes les fixtures
  • Attribut #[AsFixture(name: 'main')] permet de charger via Doctrine Fixtures
  • Les Stories peuvent avoir des dépendances (charger d'autres Stories)
  • Méthode addState() permet de nommer des instances pour les tests
  • Respecte le principe DRY : scénarios centralisés