15%

Save 15% on All Hosting Services

Test your skills and get Discount on any hosting plan

Use code:

Skills
Get Started
09.10.2024

Laravel Factories: Building Realistic Test Data with Laravel Factory Models

When developing applications with Laravel, one of the most common bottlenecks in the testing workflow is generating meaningful, realistic data. Laravel factories are classes that define a blueprint for creating Eloquent model instances, using the Faker PHP library to produce randomized but structurally valid attribute values — enabling developers to seed databases and write isolated tests without manually constructing data fixtures.

Unlike static SQL seed files or hardcoded arrays, factories are composable, stateful, and relationship-aware. They integrate directly with PHPUnit and Pest test suites, support lazy evaluation of attributes, and scale from a single model instance to thousands of records in a single method chain. If you are running Laravel on a VPS Hosting environment, factories become especially valuable during CI/CD pipeline runs, staging environment resets, and load testing scenarios where repeatable, controlled data generation is non-negotiable.

What Are Laravel Factories and Why They Matter

Laravel factories were fundamentally redesigned in Laravel 8. The older, closure-based `$factory->define()` approach was replaced with dedicated PHP classes that extend `IlluminateDatabaseEloquentFactoriesFactory`. This architectural shift introduced type safety, IDE autocompletion, and a cleaner separation between factory logic and model definitions.

Each factory class implements a `definition()` method that returns an associative array of model attributes. The factory resolves a `FakerGenerator` instance automatically, accessible via `$this->faker`, which supports over 200 locale-aware data providers — from `name()` and `safeEmail()` to `iban()`, `latitude()`, `uuid()`, and `creditCardNumber()`.

Key capabilities of the modern factory system:

  • Fluent method chaining for count, state, and relationship configuration
  • Lazy attribute resolution — closures inside `definition()` are evaluated fresh per instance
  • Factory states for modeling domain-specific variations (e.g., suspended accounts, verified users)
  • Relationship factories that recursively create parent models when needed
  • Sequences for cycling through predefined attribute sets
  • `make()` vs `create()` for in-memory vs persisted instances

Prerequisites

Before implementing factories, ensure your environment meets the following requirements:

  • Laravel 9 or later (Laravel 8 is compatible but lacks some newer sequence features)
  • PHP 8.0 or higher
  • A configured database connection in `.env` (MySQL, PostgreSQL, or SQLite for in-memory testing)
  • The `laravel/framework` package, which ships with `fakerphp/faker` as a dependency
  • Eloquent models with corresponding migration files

For teams running Laravel on managed infrastructure, VPS with cPanel provides a convenient environment to manage both the application stack and database services from a unified interface.

Step 1: Generate a Factory Class

Use the Artisan CLI to scaffold a factory file:

“`bash

php artisan make:factory UserFactory

“`

This creates `database/factories/UserFactory.php`. If you want to automatically associate the factory with a model, pass the `–model` flag:

“`bash

php artisan make:factory UserFactory –model=User

“`

Laravel resolves the factory-to-model binding through a naming convention: `UserFactory` maps to `AppModelsUser`. You can override this by setting the `protected $model` property explicitly, which is essential when your models live outside the default `AppModels` namespace.

Step 2: Define the Factory Blueprint

Open `database/factories/UserFactory.php` and define the `definition()` method:

“`php

<?php

namespace DatabaseFactories;

use AppModelsUser;

use IlluminateDatabaseEloquentFactoriesFactory;

use IlluminateSupportStr;

use IlluminateSupportFacadesHash;

class UserFactory extends Factory

{

protected $model = User::class;

public function definition(): array

{

return [

'name' => $this->faker->name(),

'email' => $this->faker->unique()->safeEmail(),

'email_verified_at' => now(),

'password' => Hash::make('password'),

'remember_token' => Str::random(10),

];

}

}

“`

Attribute-level notes:

  • `$this->faker->unique()->safeEmail()` — the `unique()` modifier maintains a per-request uniqueness registry. If you exhaust the available unique values (rare but possible with very large datasets), Faker throws a `OverflowException`. Reset it with `$this->faker->unique(true)` to clear the cache.
  • `Hash::make('password')` is preferred over `bcrypt()` directly because it respects the application's configured hashing driver (bcrypt, argon2i, argon2id).
  • `email_verified_at => now()` marks the user as already verified. Omit this field or set it to `null` to simulate an unverified account — a common state variation.

Step 3: Creating Model Instances

3.1 Persist a Single Record

“`php

$user = AppModelsUser::factory()->create();

“`

This executes an `INSERT` statement and returns a hydrated `User` Eloquent model. The returned instance reflects the actual database state, including any database-level defaults or triggers.

3.2 Persist Multiple Records

“`php

$users = AppModelsUser::factory()->count(10)->create();

“`

Returns an `IlluminateDatabaseEloquentCollection` of 10 `User` instances. Each record receives independently generated Faker values — they are not copies of a single instance.

3.3 In-Memory Instance Without Persistence

“`php

$user = AppModelsUser::factory()->make();

“`

The `make()` method instantiates the model and populates its attributes without touching the database. This is ideal for unit tests that verify model behavior, attribute casting, or accessor/mutator logic in isolation — keeping tests fast and database-independent.

3.4 Override Specific Attributes

Both `create()` and `make()` accept an array of attribute overrides:

“`php

$user = AppModelsUser::factory()->create([

'email' => 'specific@example.com',

'name' => 'Jane Doe',

]);

“`

Overrides take precedence over the `definition()` values. This is the correct pattern when a test depends on a specific, known attribute value rather than a random one.

Step 4: Factory States

States are named modifications to the base factory definition. They allow you to model distinct domain conditions without duplicating the entire factory.

4.1 Defining States

“`php

public function unverified(): static

{

return $this->state(fn (array $attributes) => [

'email_verified_at' => null,

]);

}

public function admin(): static

{

return $this->state(fn (array $attributes) => [

'is_admin' => true,

'role' => 'administrator',

]);

}

public function suspended(): static

{

return $this->state(fn (array $attributes) => [

'suspended_at' => now(),

'is_active' => false,

]);

}

“`

4.2 Applying States

“`php

// Single state

$adminUser = AppModelsUser::factory()->admin()->create();

// Stacked states — fully composable

$suspendedAdmin = AppModelsUser::factory()->admin()->suspended()->create();

“`

States are evaluated in the order they are chained. Later states overwrite conflicting keys from earlier ones, giving you predictable, layered attribute resolution.

Step 5: Sequences for Cycling Attribute Values

When you need to alternate between a defined set of values rather than random ones, use `Sequence`:

“`php

use IlluminateDatabaseEloquentFactoriesSequence;

$users = AppModelsUser::factory()

->count(6)

->state(new Sequence(

['role' => 'editor'],

['role' => 'viewer'],

['role' => 'moderator'],

))

->create();

“`

This cycles through the sequence array, assigning roles in order. With 6 users, each role is assigned twice. Sequences are invaluable for testing pagination, role-based access control, and UI rendering logic that depends on varied but controlled data distributions.

Step 6: Relationship Factories

6.1 Defining a Belongs-To Relationship

In `PostFactory.php`, reference the parent factory directly as an attribute value:

“`php

<?php

namespace DatabaseFactories;

use AppModelsPost;

use AppModelsUser;

use IlluminateDatabaseEloquentFactoriesFactory;

class PostFactory extends Factory

{

protected $model = Post::class;

public function definition(): array

{

return [

'user_id' => User::factory(),

'title' => $this->faker->sentence(),

'body' => $this->faker->paragraphs(3, true),

'slug' => $this->faker->unique()->slug(),

];

}

}

“`

When `user_id` is set to `User::factory()`, Laravel defers its evaluation. If you call `Post::factory()->create()` without providing a `user_id`, a new `User` is automatically created and its primary key is used. If you provide an existing user, the nested factory is skipped entirely.

6.2 Attaching to an Existing Parent

“`php

$user = AppModelsUser::factory()->create();

$posts = AppModelsPost::factory()->count(5)->for($user)->create();

“`

The `for()` method sets the `user_id` foreign key to the provided model's primary key, preventing unnecessary user creation. This is the correct pattern when your test already has a specific user in scope.

6.3 Has-Many Relationships

“`php

$userWithPosts = AppModelsUser::factory()

->has(AppModelsPost::factory()->count(3), 'posts')

->create();

“`

Or using the magic `hasPosts()` shorthand (resolved via the relationship method name on the model):

“`php

$userWithPosts = AppModelsUser::factory()->hasPosts(3)->create();

“`

This creates one user and three associated posts in a single, atomic operation — with all foreign keys resolved correctly.

6.4 Many-to-Many Relationships

“`php

$user = AppModelsUser::factory()

->hasAttached(

AppModelsRole::factory()->count(2),

['assigned_at' => now()]

)

->create();

“`

The `hasAttached()` method handles pivot table insertion, including any additional pivot attributes you need to populate.

Step 7: Using Factories in Tests

7.1 Feature Test with Database Assertions

“`php

use IlluminateFoundationTestingRefreshDatabase;

class UserTest extends TestCase

{

use RefreshDatabase;

public function test_user_can_be_created_with_factory(): void

{

$user = AppModelsUser::factory()->create();

$this->assertDatabaseHas('users', [

'email' => $user->email,

]);

}

public function test_unverified_user_cannot_access_dashboard(): void

{

$user = AppModelsUser::factory()->unverified()->create();

$response = $this->actingAs($user)->get('/dashboard');

$response->assertRedirect('/email/verify');

}

}

“`

Critical detail: Always use the `RefreshDatabase` or `DatabaseTransactions` trait in test classes that interact with the database. `RefreshDatabase` runs migrations fresh before the test suite and wraps each test in a transaction that is rolled back afterward, keeping tests isolated and idempotent.

7.2 Unit Test with `make()`

“`php

public function test_user_full_name_accessor(): void

{

$user = AppModelsUser::factory()->make([

'name' => 'Alice Wonderland',

]);

$this->assertEquals('Alice Wonderland', $user->name);

}

“`

No database interaction occurs. The test runs in microseconds and is suitable for high-frequency CI pipelines.

Step 8: Database Seeders with Factories

8.1 Create a Seeder

“`bash

php artisan make:seeder UserSeeder

“`

8.2 Implement the Seeder

“`php

<?php

namespace DatabaseSeeders;

use AppModelsUser;

use IlluminateDatabaseSeeder;

class UserSeeder extends Seeder

{

public function run(): void

{

User::factory()

->count(50)

->create();

}

}

“`

8.3 Composite Seeder with Relationships

“`php

<?php

namespace DatabaseSeeders;

use AppModelsUser;

use AppModelsPost;

use IlluminateDatabaseSeeder;

class DatabaseSeeder extends Seeder

{

public function run(): void

{

User::factory()

->count(20)

->has(Post::factory()->count(5), 'posts')

->create();

// Create 5 admin users with no posts

User::factory()->count(5)->admin()->create();

}

}

“`

8.4 Run the Seeder

“`bash

Run a specific seeder

php artisan db:seed –class=UserSeeder

Run all seeders defined in DatabaseSeeder

php artisan db:seed

Migrate fresh and seed in one command (common in staging resets)

php artisan migrate:fresh –seed

“`

The `migrate:fresh –seed` command is the standard workflow for resetting a staging or development database to a known, populated state. On Dedicated Servers, this pattern is frequently used before QA cycles to ensure a clean, reproducible environment.

Advanced Patterns and Edge Cases

Lazy Attributes and Dependent Values

Faker values inside `definition()` are re-evaluated for each factory call. However, if you need one attribute to depend on another, use a closure:

“`php

public function definition(): array

{

$firstName = $this->faker->firstName();

$lastName = $this->faker->lastName();

return [

'first_name' => $firstName,

'last_name' => $lastName,

'email' => strtolower("{$firstName}.{$lastName}@example.com"),

'username' => strtolower("{$firstName}{$lastName}") . $this->faker->numerify('###'),

];

}

“`

This ensures `email` and `username` are derived from the same name values, producing internally consistent records.

Avoiding the `unique()` Overflow Trap

When generating large datasets (10,000+ records in a single factory call), `$this->faker->unique()->safeEmail()` can exhaust Faker's uniqueness pool and throw an `OverflowException`. Mitigate this by appending a UUID or timestamp to the generated value:

“`php

'email' => $this->faker->safeEmail() . '.' . $this->faker->uuid() . '@test.com',

“`

This guarantees uniqueness at scale without relying on Faker's internal uniqueness registry.

Factory Callbacks: `afterMaking` and `afterCreating`

Use callbacks to perform post-creation logic that cannot be expressed as a simple attribute:

“`php

public function configure(): static

{

return $this->afterCreating(function (User $user) {

$user->profile()->create([

'bio' => $this->faker->paragraph(),

'avatar' => $this->faker->imageUrl(200, 200, 'people'),

]);

});

}

“`

The `configure()` method is called once when the factory is instantiated. `afterCreating` runs after the model is persisted, making it suitable for creating related models that require the parent's primary key.

Testing Email Functionality

When factories generate users with email addresses, integration tests that verify email dispatch benefit from a dedicated Email Hosting environment configured with a sandbox SMTP server, preventing accidental delivery of test emails to real addresses.

`create()` vs `make()` vs `makeMany()` vs `createMany()` — Comparison

MethodPersists to DBReturnsBest Use Case
`create()`YesSingle model instanceFeature tests, seeders
`create(['key' => 'val'])`YesSingle model instanceTests requiring specific known values
`count(n)->create()`YesCollection of n modelsBulk seeding, pagination tests
`make()`NoSingle model instanceUnit tests, accessor/mutator testing
`make(['key' => 'val'])`NoSingle model instanceFast unit tests with controlled attributes
`count(n)->make()`NoCollection of n modelsIn-memory collection testing
`createMany([…])`YesCollectionBatch creation with distinct attribute sets
`makeMany([…])`NoCollectionBatch in-memory instances

Factory States vs. Attribute Overrides — When to Use Each

ScenarioRecommended Approach
Reusable domain condition (e.g., "admin user")Named state method
Test-specific one-off valueAttribute override in `create()`
Cycling through a predefined set`Sequence`
Post-creation side effects`afterCreating` callback
Dependent attribute valuesClosure-based attributes in `definition()`
Relationship population`has()`, `for()`, `hasAttached()`

Practical Decision Checklist

Before writing a factory or test that uses one, work through these checkpoints:

  • Is the test database-dependent? If not, use `make()` and avoid `RefreshDatabase` overhead.
  • Does the test require a specific attribute value? Pass it as an override to `create()` — do not hardcode it in the factory `definition()`.
  • Are you testing role-based behavior? Define named states rather than scattering `create(['is_admin' => true])` across multiple test files.
  • Are you seeding a staging environment? Use `migrate:fresh –seed` and ensure your `DatabaseSeeder` composes all sub-seeders in the correct dependency order (parents before children).
  • Are you generating more than 5,000 records? Avoid `unique()` on high-cardinality fields; use UUID-suffixed values instead.
  • Do your models have `afterCreating` callbacks that hit external services? Mock those services in your test setup or use `make()` to bypass the callback entirely.
  • Are you running tests in parallel? Use `DatabaseTransactions` instead of `RefreshDatabase` to avoid migration conflicts across parallel workers, or configure separate database connections per worker.

For teams managing multiple Laravel applications across environments, VPS Control Panels provide the infrastructure visibility needed to monitor database performance during large seeding operations and test runs.

FAQ

What is the difference between `create()` and `make()` in Laravel factories?

`create()` persists the model to the database and returns a hydrated Eloquent instance. `make()` builds the model in memory without any database interaction. Use `make()` for pure unit tests to keep them fast and isolated; use `create()` when the test must verify database state.

Can Laravel factories handle polymorphic relationships?

Yes. Define a `morphTo` relationship by setting the `*_type` and `*_id` morph columns directly in the factory `definition()`, or use `afterCreating` to attach polymorphic relations after the parent model is persisted. There is no built-in `hasMorphedByMany()` shorthand, so explicit attribute setting is the most reliable approach.

How do you prevent factory-generated emails from being sent during tests?

Set `MAIL_MAILER=array` or `MAIL_MAILER=log` in your `.env.testing` file. This routes all mail through Laravel's array or log driver, capturing messages in memory or writing them to the log file without dispatching to an SMTP server. You can then assert on `Mail::assertSent()` in your tests.

Why does `faker->unique()->safeEmail()` throw an `OverflowException` in large datasets?

Faker's `unique()` modifier maintains an in-memory registry of previously generated values. When the pool of structurally valid unique values is exhausted — which can happen with tens of thousands of records — it throws `OverflowException`. The fix is to append a UUID or random string to the base email value, ensuring uniqueness without relying on Faker's registry.

Should factories be used in production seeders?

Factories are designed for development and testing environments. For production seeding (e.g., populating lookup tables, default roles, or configuration records), use dedicated seeder classes with hardcoded, deterministic values. Factories that depend on Faker should never run against a production database, as they introduce unpredictable, non-auditable data.

15%

Save 15% on All Hosting Services

Test your skills and get Discount on any hosting plan

Use code:

Skills
Get Started