Alice and Faker: Generating Symfony Test Fixtures

Generating Fake Data in Symfony: Why and How
Every serious Symfony project relies on a controlled dataset. Whether you need to populate a development environment, run a functional test suite or prepare a client demo, generating fake data is an essential link in the workflow. Two tools dominate this space in the PHP ecosystem: the Alice bundle (paired with FakerPHP) and Foundry. This article explores both approaches, from YAML fundamentals to architectural strategies for managing data at scale.
Alice and FakerPHP: The Foundations
The Alice bundle rests on two complementary building blocks. Nelmio Alice provides a declarative fixture engine that reads YAML, JSON or PHP files to instantiate Doctrine objects. The changes introduced by Doctrine ORM 3.0 (in French) directly affect how you define the entities used in fixtures. FakerPHP is the realistic data generator: names, addresses, dates, texts, bank identifiers. The Symfony integration comes from hautelook/alice-bundle, which registers fixtures automatically and exposes a CLI command to load them.
Managing dependencies with Composer (in French) makes installation trivial:
composer require --dev nelmio/alice hautelook/alice-bundle
The bundle is registered automatically through Symfony Flex. Fixture files go in the project's fixtures/ directory.
Declaring Fixtures in YAML
Alice's YAML syntax is its main strength. It lets you describe a complete object graph in a readable way, without writing a single line of PHP:
App\Entity\User:
user_{1..20}:
firstName: '<firstName()>'
lastName: '<lastName()>'
email: '<email()>'
password: '$2y$13$fixedHashForFixtures'
roles: ['ROLE_USER']
createdAt: '<dateTimeBetween("-1 year", "now")>'
The password here is a precomputed bcrypt hash. Alice provides no native formatter for hashing passwords. In practice, use an Alice processor that injects Symfony's UserPasswordHasherInterface to hash passwords dynamically when the fixtures load.
The {1..20} notation generates twenty instances. Expressions between angle brackets call FakerPHP methods. This declarative approach considerably reduces noise in the code and keeps fixtures accessible even to non-technical profiles who need to understand the data structure.
You load the data into the database with:
php bin/console hautelook:fixtures:load
By default, the command purges the database before inserting. The --append option adds data incrementally.
Need support on your project?
Let's talkReferences Between Entities and Relational Graphs
Alice's power shows in how it handles relations. The reference system lets you weave a consistent object graph across files:
App\Entity\Category:
category_php:
name: 'PHP'
slug: 'php'
category_symfony:
name: 'Symfony'
slug: 'symfony'
category_javascript:
name: 'JavaScript'
slug: 'javascript'
App\Entity\Article:
article_{1..50}:
title: '<sentence(6)>'
content: '<paragraphs(5, true)>'
author: '@user_<numberBetween(1, 20)>'
category: '@category_*'
publishedAt: '<dateTimeBetween("-6 months", "now")>'
isPublished: '<boolean(80)>'
The @user_<numberBetween(1, 20)> syntax dynamically resolves a reference to an existing user. The @category_* wildcard picks randomly among all defined categories. This mechanism supports ManyToOne, OneToMany and ManyToMany relations, so you can model complete business domains without imperative code.
Custom Providers for Business Logic
FakerPHP covers common data types, but every business domain has its own specifics. Alice lets you plug in custom providers, PHP classes that extend Faker\Provider\Base:
namespace App\DataFixtures\Provider;
use Faker\Provider\Base;
class ArticleProvider extends Base
{
private const TAGS = [
'symfony', 'php', 'docker', 'api-platform',
'react', 'typescript', 'postgresql', 'redis',
];
public function articleTag(): string
{
return self::TAGS[array_rand(self::TAGS)];
}
public function articleSlug(): string
{
return strtolower(
str_replace(' ', '-', $this->generator->sentence(4))
);
}
}
You register the provider with the nelmio_alice.faker.provider service tag:
services:
App\DataFixtures\Provider\ArticleProvider:
tags: ['nelmio_alice.faker.provider']
You can then use the methods directly in YAML files:
App\Entity\Article:
article_{1..50}:
slug: '<articleSlug()>'
tags: ['<articleTag()>', '<articleTag()>']
This extensibility is valuable for making sure test data respects business invariants: product reference formats, internal codes, hierarchical structures specific to the domain.
Foundry: The Programmatic Approach
Foundry takes the opposite stance from Alice with an entirely PHP-based approach. Each entity has a factory that encapsulates its creation logic:
composer require --dev zenstruck/foundry
namespace App\Factory;
use App\Entity\User;
use Zenstruck\Foundry\Persistence\PersistentProxyObjectFactory;
final class UserFactory extends PersistentProxyObjectFactory
{
public static function class(): string
{
return User::class;
}
protected function defaults(): array
{
return [
'firstName' => self::faker()->firstName(),
'lastName' => self::faker()->lastName(),
'email' => self::faker()->unique()->email(),
'password' => 'password123',
'roles' => ['ROLE_USER'],
];
}
}
Creating objects is fluent and composable:
UserFactory::createMany(20);
ArticleFactory::createOne([
'author' => UserFactory::random(),
'category' => CategoryFactory::createOne(['name' => 'PHP']),
]);
To keep data consistent, your choice of identifier type (INT, UUID or ULID) (in French) directly affects fixture generation and test performance. The ResetDatabase trait isolates each PHPUnit test with an automatic transactional rollback, building on the principles described in the official Symfony testing documentation. This removes side effects between tests without reloading all fixtures:
use Zenstruck\Foundry\Test\ResetDatabase;
class ArticleControllerTest extends WebTestCase
{
use ResetDatabase;
public function testListArticles(): void
{
ArticleFactory::createMany(5);
$this->client->request('GET', '/articles');
$this->assertResponseIsSuccessful();
}
}
Side-by-Side Comparison
| Feature | Alice Faker | Foundry |
|---|---|---|
| Definition format | YAML, JSON, PHP | PHP (factories) |
| Random generation | Via FakerPHP | Via FakerPHP |
| Relations between entities | YAML references | Fluent PHP methods |
| Doctrine integration | Via DoctrineFixturesBundle | Native |
| Database reset in tests | Manual | ResetDatabase trait |
| Maintenance | Active | Very active |
Alice excels at readability and rapid prototyping. Foundry becomes the better choice once automated tests are the center of gravity of the project.
Fixtures by Environment and CI Integration
A mature project does not load the same data in development, staging and CI. With Alice, segmentation comes from how you organize files: a fixtures/dev/ directory for large datasets meant for local development, and a minimal fixtures/test/ for CI. The hautelook bundle lets you configure paths per environment in hautelook_alice.yaml.
With Foundry, segmentation comes naturally: each test instantiates exactly the objects it needs. For development, an AppFixtures class calls the factories with suitable volumes.
In CI, your fixture strategy directly affects pipeline duration. Loading 10,000 entities for a test that checks 3 is a waste. Foundry factories encourage strict decoupling between the global dataset and the needs of each test. With Alice, discipline falls to the developer: split the files and load only the relevant subset.
Data Strategy at Scale
Beyond tooling, what sets a well-architected project apart is its test data management strategy. Several questions come up at the scale of a team or an organization.
Fixture Inheritance and Composition
On a monolith or a project with multiple bounded contexts, duplicating fixtures across modules becomes a maintenance problem. Alice lets you spread files across domains and compose them through imports. Foundry pushes you toward reusable factories and shared traits. In both cases, the goal is to establish a common data foundation (users, roles, base configuration) on which each module stacks its own specifics.
Seeding Staging Environments
Staging poses a different challenge from local development: it must reflect production reality in terms of data volume and distribution, without exposing personal data. Classic fixtures are not enough. The recommended approach combines an anonymized production dump with complementary fixtures for edge cases. Tools like doctrine/dbal or dedicated SQL scripts let you mask sensitive data (emails, names, addresses) while preserving the relational structure and realistic volumes.
Anonymization and GDPR Compliance
As soon as production data feeds a non-production environment, anonymization becomes a legal question. For more advanced anonymization needs, DbToolsBundle offers a dedicated database anonymization solution (in French) built into Symfony. FakerPHP can serve as an anonymization building block by replacing sensitive fields with generated values. But be careful: naive anonymization (replacing every email with <email()>) is not enough if the data remains correlatable through other fields. A robust strategy requires analyzing the data model to identify every directly or indirectly identifying attribute, then applying a consistent transformation that preserves statistical usefulness without allowing re-identification.
Fixtures and Performance Tests
Load tests need data volumes close to production. Neither Alice nor Foundry is designed to insert millions of rows efficiently. For these scenarios, favor raw SQL scripts or tools like PostgreSQL's COPY, generating the data with FakerPHP in an intermediate CSV format. YAML fixtures and PHP factories remain reserved for functional and integration tests, where clarity matters more than throughput.
Which Tool to Choose
For a new Symfony project, Foundry is the natural choice. Its factory approach fits directly into the PHPUnit workflow, its transactional isolation speeds up test suites, and its very active development keeps it in step with Symfony and Doctrine changes.
Alice remains relevant for existing projects that already use it, for teams that prefer a declarative approach, or when non-developers need to understand and edit datasets. You can migrate from Alice to Foundry gradually, factory by factory, with no big bang.
In every case, the tool is only a means. The real value lies in a coherent data strategy: minimal, targeted fixtures for tests, large and realistic datasets for development, and rigorous anonymization for anything that touches production data. This rigor in test data management is an integral part of our approach to custom web development (in French).
Going Further
- The most widely used bundles in Symfony projects (in French), an overview of the essential bundles in the Symfony ecosystem
- Using Composer in Symfony development, practical tips for managing your PHP dependencies
- How PHPStan can improve the quality of your PHP code (in French), strengthen your code quality with static analysis
- Official nelmio/alice documentation on GitHub, the complete guide to the Alice bundle
- Official zenstruck/foundry documentation, the Foundry bundle in the Symfony documentation
- Symfony documentation on fixtures with Doctrine, the official guide to loading test data
Have a project in mind?
Our team replies within 48 hours to look at your needs and suggest a suitable approach.
Contact usRelated articles

Symfony and PHP End of Life: The Action Plan
Symfony and PHP end-of-life calendar for 2026, real consequences (CVEs, NIS2, GDPR) and three exit strategies depending on how critical your application is.
Read more →
Elasticsearch vs Algolia for a Symfony Project
Elasticsearch and Algolia meet different needs. A hands-on technical comparison to help you pick the right search engine for your Symfony project and team.
Read more →
Sylius vs PrestaShop: Which E-Commerce Platform?
Sylius and PrestaShop are two open source PHP e-commerce platforms. A technical comparison of architecture, flexibility, security and upkeep to help you choose.
Read more →