Symfony-Tests mit Zenstruck Foundry auf das nächste Level bringen

Zenstruck Foundry hat die Art und Weise, wie wir Tests in Symfony schreiben, revolutioniert. In diesem Beitrag erfährst du, wie uns ausdrucksstarke Factories, isolierte Testdaten und ein reibungsloseres Entwicklererlebnis dabei geholfen haben, unseren Test-Workflow zu optimieren und die Produktivität zu steigern.
Zenstruck Foundry ist eine leistungsstarke Bibliothek, die die Erstellung von Testdaten in Symfony-Anwendungen deutlich vereinfacht. Im Kern bietet Foundry ausdrucksstarke, automatisch vervollständigte Factories für Doctrine-Entitäten, mit denen Entwickler Daten schnell und ohne viel Boilerplate erzeugen können.
Dank der Integration mit Faker, Unterstützung für Doctrine ORM/ODM und Features wie „Stories“ steigert Foundry die Produktivität der Entwickler und erhöht die Zuverlässigkeit von Tests erheblich.
Eine Factory für eine Doctrine-Entität erstellen
Stellen wir uns vor, wir haben eine User
Entity:
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;
}
Wir können jetzt eine Factory dafür generieren:
bin/console make:factory App\\Entity\\User
Dieser Befehl erstellt die folgende UserFactory
Klasse:
// 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;
}
}
Verwendung von Factories in Tests
Mit der erstellten Factory kannst du nun Benutzer in deinen Testfällen erzeugen:
public function testGetEmail(): void
{
$user = UserFactory::createOne([
'email' => $expected = 'john@doe.com'
]);
self::assertSame($expected, $user->getEmail());
}
Du brauchst mehr Benutzer?
$users = UserFactory::createMany(5);
Einführung in Stories
Stories sind eine hervorragende Möglichkeit, wiederverwendbare Datenszenarien zu definieren. Wenn du z. B. möchtest, dass ein Standard-Admin-Benutzer immer existiert, kannst du ihn in einer Story definieren:
// 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',
]);
}
}
Diese Story kannst du über Doctrine Fixtures laden:
bin/console doctrine:fixtures:load
Achte darauf, dass der Foundry Loader in der FoundryBundle-Konfiguration registriert ist, falls erforderlich.
Eingebaute Factories
Foundry bringt auch spezialisierte Factories für fortgeschrittene Anwendungsfälle mit:
ArrayFactory
: Zum Erzeugen von Arrays, z. B. zur Simulation von API-Antworten.ObjectFactory
: Zum Erzeugen von Objekten, ohne sie zu persistieren.PersistentProxyObjectFactory
: Hilfreich, wenn erstellte Objekte direkt in der Datenbank gespeichert und als Proxy verwendet werden sollen.
Testing von Value Objects und Aggregates in DDD
Ein besonderes Highlight von Zenstruck Foundry ist die nahtlose Unterstützung komplexer Domänenmodelle, insbesondere Value Objects und Aggregates im Sinne des Domain-Driven Design (DDD). Foundry vereinfacht die Erstellung solcher Entitäten und ihrer zugehörigen Value Objects – ideal für das Testen anspruchsvoller Architekturen.
➡ Mehr zu den Grundlagen von Domain-Driven Design kannst du hier nachlesen.
Ein Beispiel: Du möchtest ein Value Object wie Price
testen. Mit einer passenden Factory kannst du realistische Daten generieren und in deine Tests einfließen lassen:
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;
}
}
So kannst du das Verhalten von Aggregates testen, ohne dich um die komplexe Erstellung oder Verwaltung der Daten kümmern zu müssen. Foundry hilft dir, realistische Domänenmodelle zu simulieren und sicherzustellen, dass die Interaktionen zwischen Aggregates und Value Objects realitätsnah funktionieren.
Fazit
Zenstruck Foundry ist ein unverzichtbares Werkzeug in unserem Symfony-Werkzeugkasten geworden. Es reduziert Boilerplate-Code, verbessert die Testzuverlässigkeit und erhöht die Produktivität. Die Möglichkeit, Stories für wiederverwendbare Szenarien zu nutzen und fortgeschrittene Factories für komplexe Anforderungen bereitzustellen, macht Foundry zu einem Must-Have für jedes Symfony-Projekt.
Darüber hinaus passt sich Foundry hervorragend an die Prinzipien des Domain-Driven Design an – ideal also für Tests komplexer Domänenmodelle mit Aggregates und Value Objects.