Сидеры и фабрики: тестовые данные

Сидеры и фабрики наполняют базу тестовыми данными автоматически, чтобы не вводить десятки записей вручную при каждом сбросе схемы.

Суть: фабрика описывает, как выглядит одна фейковая запись, а сидер массово создаёт такие записи. Команда php artisan db:seed запускает наполнение.

Когда вы разрабатываете приложение, пустая база бесполезна: нечего показать в списке, не на чём проверить пагинацию или поиск. Вводить данные руками через формы — медленно и скучно, особенно если вы часто пересоздаёте схему командой migrate:fresh. Сидеры и фабрики решают это: они генерируют реалистичные тестовые данные одной командой.

Связка работает так. Фабрика — это шаблон одной записи: она говорит, какие поля заполнить и какими случайными значениями (имя, email, цена). За правдоподобные данные отвечает встроенная библиотека Faker. Сидер — это сценарий наполнения: он вызывает фабрику нужное число раз и кладёт записи в базу.

Фабрика

php artisan make:factory ProductFactory
<?php
namespace Database\Factories;

use Illuminate\Database\Eloquent\Factories\Factory;

class ProductFactory extends Factory
{
    // как выглядит одна фейковая запись
    public function definition(): array
    {
        return [
            'name'      => fake()->words(2, true),
            'price'     => fake()->randomFloat(2, 100, 9999),
            'is_active' => fake()->boolean(80),
        ];
    }
}

Сидер

<?php
namespace Database\Seeders;

use App\Models\Product;
use Illuminate\Database\Seeder;

class DatabaseSeeder extends Seeder
{
    public function run(): void
    {
        // создать 50 случайных товаров
        Product::factory()->count(50)->create();
    }
}
# запустить наполнение
php artisan db:seed

# пересоздать схему и сразу наполнить
php artisan migrate:fresh --seed

Как работает под капотом

Метод Product::factory()->count(50)->create() 50 раз вызывает definition(), каждый раз получая новый набор случайных полей от Faker, и сохраняет записи в базу через Eloquent. Фабрики можно комбинировать с отношениями: создать пользователя сразу с тремя заказами одним вызовом. Это делает подготовку сложных тестовых сценариев тривиальной.

  Product::factory()->count(3)->create()
        |
        v
  definition() -> {name, price, is_active}   (запись 1)
  definition() -> {name, price, is_active}   (запись 2)
  definition() -> {name, price, is_active}   (запись 3)
        |
        v
  INSERT в таблицу products (3 строки)

Смоделируем фабрику на Python: генерируем N случайных записей по шаблону.

Попробуй сам ▶

import random

words = ['Кофе', 'Лампа', 'Книга', 'Ручка', 'Стол']

def definition():                  # шаблон одной записи
    return {
        'name':  random.choice(words),
        'price': round(random.uniform(100, 9999), 2),
        'active': random.random() < 0.8,
    }

def factory(count):                # фабрика: создать count записей
    return [definition() for _ in range(count)]

for product in factory(3):
    print(product)

Частые ошибки

  • Запускать сидеры на проде. Фейковые данные на боевом сервере — мусор; сидеры для dev и тестов.
  • Фиксированные значения вместо fake(). Одинаковые записи плохо проверяют поиск, сортировку и уникальность.
  • Забыть зарегистрировать сидер. Кастомные сидеры вызывают из DatabaseSeeder::run() через $this->call().

Best practices

  • Используйте фабрики и в сидерах, и в автотестах — это переиспользуемый источник данных.
  • Для уникальных полей применяйте fake()->unique().
  • Описывайте состояния (states) фабрики для разных сценариев: активные/неактивные, админ/обычный.

У фабрик есть мощный механизм состояний (states), который делает их по-настоящему гибкими. Состояние — это именованная модификация набора полей: например, метод inactive() переопределяет is_active на false, а expensive() задаёт высокую цену. Вызов Product::factory()->inactive()->count(5)->create() создаст пять неактивных товаров. Это бесценно в автотестах, где нужно воспроизвести конкретный сценарий. Фабрики также элегантно работают со связями: запись User::factory()->has(Order::factory()->count(3))->create() создаст пользователя сразу с тремя заказами, корректно проставив внешние ключи. Благодаря этому подготовка даже сложных графов данных умещается в одну выразительную строку, а тесты остаются читаемыми и не зависят от ручного наполнения базы.

Итог: фабрики описывают одну фейковую запись, сидеры наполняют базу пачками, а вместе они избавляют от ручного ввода данных при разработке и тестировании. Дальше — обработка форм и валидация ввода.

Проверьте себя
1. В чём разница между фабрикой и сидером?
AЭто одно и то же
BФабрика описывает одну фейковую запись, сидер массово создаёт записи
CФабрика удаляет данные, сидер создаёт
DСидер — это таблица
2. Какая команда пересоздаёт схему и сразу наполняет её тестовыми данными?
Aphp artisan serve
Bphp artisan migrate:fresh --seed
Cphp artisan make:model
Dphp artisan route:list