Boostez vos tests Symfony avec Zenstruck Foundry

Zenstruck Foundry a révolutionné notre manière d’écrire des tests dans Symfony. Dans cet article, vous apprendrez comment des fabriques expressives, des données de test isolées et une expérience développeur plus fluide nous ont permis d’optimiser nos flux de tests et d’améliorer la productivité.
Qu’est-ce que Zenstruck Foundry ?
Zenstruck Foundry est une bibliothèque très utile conçue pour simplifier la création de données de test dans les applications Symfony. Au cœur de Foundry, on retrouve des factories expressives et auto-complétées pour les entités Doctrine, permettant aux développeurs de générer rapidement des données avec un minimum de code répétitif (boilerplate).
Avec le support de Faker, une intégration complète avec Doctrine ORM/ODM, et des fonctionnalités comme les stories, Foundry améliore considérablement la productivité des développeurs et la fiabilité des tests.
Créer une factory pour une entité Doctrine
Imaginons que nous ayons une entité User
:
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity]
class User
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column(type: 'integer')]
private ?int $id = null;
#[ORM\Column(type: 'string')]
private string $email;
#[ORM\Column(type: 'string')]
private string $name;
}
On peut alors générer une factory pour cette entité :
bin/console make:factory App\\Entity\\User
Cette commande crée la classe UserFactory
suivante :
// src/Factory/UserFactory.php
namespace App\Factory;
use App\Entity\User;
use Zenstruck\Foundry\Persistence\PersistentObjectFactory;
use Zenstruck\Foundry\Proxy;
/**
* @extends PersistentObjectFactory<User>
*/
final class UserFactory extends PersistentObjectFactory
{
protected function defaults(): array
{
return [
'email' => self::faker()->email(),
'name' => self::faker()->name(),
];
}
protected static function class(): string
{
return User::class;
}
}
Utiliser les factories dans les tests
Une fois la factory créée, vous pouvez l’utiliser dans vos tests pour générer des utilisateurs :
public function testGetEmail(): void
{
$user = UserFactory::createOne([
'email' => $expected = 'john@doe.com'
]);
self::assertSame($expected, $user->getEmail());
}
Et si vous avez besoin de plusieurs utilisateurs, vous pouvez en créer de cette façon :
$users = UserFactory::createMany(5);
Une introduction aux Stories
Les stories sont un excellent moyen de définir des scénarios de données réutilisables. Par exemple, si vous souhaitez qu’un utilisateur administrateur existe toujours par défaut, vous pouvez le définir dans une story :
// src/Story/DefaultUsersStory.php
namespace App\Story;
use App\Factory\UserFactory;
use Zenstruck\Foundry\Story;
final class DefaultUsersStory extends Story
{
public function build(): void
{
UserFactory::createOne([
'email' => 'admin@example.com',
'name' => 'Administrator',
]);
}
}
Vous pouvez ensuite charger cette story avec les fixtures Doctrine :
bin/console doctrine:fixtures:load
Assurez-vous que le Foundry loader est bien enregistré dans la configuration de FoundryBundle
si nécessaire.
Factories intégrées
Foundry propose également des factories spécialisées pour des cas d’usage avancés :
ArrayFactory
: pour générer des tableaux de données (par exemple pour simuler des réponses API).ObjectFactory
: pour créer des objets sans les persister.PersistentProxyObjectFactory
: utile lorsque vous souhaitez que les objets soient immédiatement persistés en base et accessibles via un Proxy.
Tester des Value Objects et des Agrégats en DDD
L’une des grandes forces de Zenstruck Foundry est sa capacité à gérer des modèles de domaine complexes, y compris les Value Objects et Agrégats dans le cadre du Domain-Driven Design (DDD). Foundry facilite la création des entités et de leurs objets de valeur associés, ce qui en fait un excellent choix pour tester des architectures DDD sophistiquées.
➡ Découvrez les bases du Domain-Driven Design dans mon blog post précédent
Par exemple, imaginons que vous souhaitiez tester un Value Object comme Price
. Grâce aux factories de Foundry, vous pouvez rapidement injecter des données réalistes dans vos objets métier.
final readonly class Price
{
public function __construct(
public int $amount,
public string $currency,
) {
}
}
use Zenstruck\Foundry\Object\Instantiator\ObjectFactory;
final class PriceFactory extends ObjectFactory
{
protected function defaults(): array
{
return [
'amount' => self::faker()->numberBetween(100, 1000),
'currency' => self::faker()->randomElement(['€', '$']),
];
}
protected static function getClass(): string
{
return Price::class;
}
}
Cela vous permet ainsi de tester le comportement des agrégats en DDD sans vous soucier de la complexité liée à la création ou la gestion des données sous-jacentes. Foundry vous aide à simuler des modèles métiers réalistes pour garantir des interactions cohérentes entre agrégats et objets de valeur.
Conclusion
Zenstruck Foundry est devenu un outil incontournable dans notre stack Symfony. Il réduit la répétition dans le code, améliore la fiabilité des tests et booste la productivité. Les stories pour les scénarios réutilisables et les factories avancées pour des besoins complexes en font un excellent atout pour n'importe quel projet Symfony.
En plus, sa compatibilité avec les principes du DDD permet de tester facilement des Value Objects et des Aggregates, ce qui en fait un très bon choix choix pour des projets avec des modèles de domaine complexes.