Curso de Laravel
Factories en Laravel: generar datos de prueba
Por Víctor Peña · Publicado el
Hola, ¿cómo están? Continuando con el curso de Laravel, hoy veremos los factories: plantillas que generan registros de prueba con datos verosímiles.
Son de esas herramientas que parecen un lujo hasta que las usas y ya no puedes trabajar sin ellas.
¡Empecemos!
Para qué sirven
Necesitas cincuenta pacientes para probar la paginación. Puedes escribirlos a mano, y terminar con «Paciente 1», «Paciente 2» y teléfonos como «111111».
El problema de esos datos no es la pereza: es que no se parecen a los reales. Con nombres de una palabra nunca vas a descubrir que tu tabla se desmaqueta con apellidos largos, ni que la búsqueda falla con tildes.
Un factory genera datos que sí se parecen a los de verdad.
Crear un factory
php artisan make:factory PacienteFactory
O junto con el modelo:
php artisan make:model Paciente -f
Se crea en database/factories/:
<?php
namespace Database\Factories;
use Illuminate\Database\Eloquent\Factories\Factory;
class PacienteFactory extends Factory
{
public function definition(): array
{
return [
'cedula' => fake()->unique()->numerify('#######'),
'nombre' => fake()->firstName(),
'apellido' => fake()->lastName(),
'fecha_nacimiento' => fake()->dateTimeBetween('-80 years', '-1 year'),
'telefono' => fake()->numerify('7#######'),
'correo' => fake()->unique()->safeEmail(),
];
}
}
El método definition() devuelve un arreglo con los valores por defecto de un registro.
Faker: el generador de datos
fake() da acceso a Faker, una librería que produce datos falsos de casi cualquier tipo:
fake()->name(); // Juan Pérez
fake()->firstName();
fake()->lastName();
fake()->email();
fake()->safeEmail(); // dominio de ejemplo, nunca real
fake()->phoneNumber();
fake()->address();
fake()->city();
fake()->sentence();
fake()->paragraph();
fake()->randomElement(['programada', 'atendida', 'cancelada']);
fake()->numberBetween(1, 100);
fake()->randomFloat(2, 100, 500);
fake()->boolean(70); // true el 70 % de las veces
fake()->date();
fake()->dateTimeBetween('-1 year', 'now');
fake()->unique()->numerify('#######');
Dos que conviene destacar:
safeEmail() genera correos con dominios reservados para ejemplos. Es importante: si tu sistema envía correos en pruebas, no querrás que lleguen a direcciones reales.
unique() garantiza que no se repita, algo imprescindible en columnas con restricción UNIQUE como la cédula.
Faker en español
Por defecto genera nombres en inglés. Para que sean en español, en el .env:
APP_FAKER_LOCALE=es_ES
Hay locales por país. es_ES para España, es_PE para Perú, es_MX para México. No existe uno específico de Bolivia, pero cualquiera de esos produce nombres mucho más naturales que el inglés.
Usar el factory
// Un registro
Paciente::factory()->create();
// Cincuenta
Paciente::factory()->count(50)->create();
// Sobrescribiendo algún valor
Paciente::factory()->create([
'nombre' => 'Ana',
'ciudad' => 'Cochabamba',
]);
// Sin guardar en la base
$paciente = Paciente::factory()->make();
La diferencia entre create() y make() importa: el primero guarda en la base de datos, el segundo solo construye el objeto en memoria. make() es útil en pruebas donde no necesitas persistir nada.
Estados: variantes del factory
Aquí es donde los factories dejan de ser cómodos y pasan a ser potentes.
Un estado es una variante con nombre:
class CitaFactory extends Factory
{
public function definition(): array
{
return [
'fecha' => fake()->dateTimeBetween('now', '+2 months'),
'hora' => fake()->time('H:i:s'),
'costo' => fake()->randomFloat(2, 150, 500),
'estado' => 'programada',
];
}
public function atendida(): static
{
return $this->state(fn (array $atributos) => [
'estado' => 'atendida',
'fecha' => fake()->dateTimeBetween('-3 months', 'now'),
'diagnostico' => fake()->paragraph(),
]);
}
public function cancelada(): static
{
return $this->state(fn (array $atributos) => [
'estado' => 'cancelada',
]);
}
}
Y al usarlos:
Cita::factory()->count(20)->create(); // programadas
Cita::factory()->atendida()->count(30)->create(); // atendidas
Cita::factory()->cancelada()->create();
Fíjate en el detalle del estado atendida(): además de cambiar el estado, mueve la fecha al pasado y añade un diagnóstico. Una cita atendida con fecha futura sería incoherente, y el factory se encarga de que los datos tengan sentido.
Eso es lo que hace valiosos los estados: encapsulan qué significa que un registro esté en cierta situación.
Factories con relaciones
Un factory puede crear los registros relacionados que necesita:
class CitaFactory extends Factory
{
public function definition(): array
{
return [
'paciente_id' => Paciente::factory(),
'doctor_id' => Doctor::factory(),
'fecha' => fake()->dateTimeBetween('now', '+2 months'),
'costo' => fake()->randomFloat(2, 150, 500),
];
}
}
Con eso, Cita::factory()->create() crea también el paciente y el doctor. No hay que preocuparse por las claves foráneas.
Crear un padre con sus hijos
Refugio::factory()
->has(Mascota::factory()->count(5))
->create();
Un refugio con cinco mascotas, todas apuntando correctamente.
O al revés
Cita::factory()
->for(Paciente::factory()->create(['nombre' => 'Ana']))
->count(3)
->create();
Tres citas para la misma paciente.
Estos dos métodos —has() y for()— son los que hacen que armar un escenario de prueba completo sea cuestión de tres líneas.
Combinar con seeders
Los factories y los seeders se complementan: el factory define cómo se ve un registro, el seeder decide cuántos y cómo se relacionan.
class DatabaseSeeder extends Seeder
{
public function run(): void
{
// Datos reales del sistema, escritos a mano
$this->call(EspecialidadSeeder::class);
// Datos de prueba, generados
Doctor::factory()->count(8)->create();
Paciente::factory()
->count(50)
->has(Cita::factory()->count(3))
->create();
}
}
Cincuenta pacientes con tres citas cada uno: ciento cincuenta citas coherentes en cuatro líneas.
Factories en las pruebas
Este es su uso más importante, aunque sea el que menos se ve al principio.
it('muestra solo las citas programadas', function () {
Cita::factory()->count(3)->create();
Cita::factory()->cancelada()->count(2)->create();
$respuesta = $this->get('/citas');
$respuesta->assertOk();
$respuesta->assertViewHas('citas', fn ($citas) => $citas->count() === 3);
});
Cada prueba construye exactamente el escenario que necesita, sin depender de datos que alguien dejó en la base. Es lo que hace que las pruebas sean fiables y repetibles.
Errores comunes
- No usar
unique()en columnas con restricción de unicidad, y fallar aleatoriamente al crear muchos registros. - Usar
email()en lugar desafeEmail(), con riesgo de enviar correos a direcciones reales. - Datos incoherentes: una cita atendida con fecha futura. Los estados existen para esto.
- Ejecutar factories en producción. Solo en desarrollo y pruebas.
- Definir relaciones a mano en lugar de usar
has()yfor(). - Olvidar
APP_FAKER_LOCALEy trabajar con nombres en inglés.
Para cerrar
Los factories eliminan la parte más tediosa de desarrollar: conseguir datos con los que probar. Y hacen algo más importante, que es generar datos realistas, con los que sí descubres los problemas antes de que lo haga el cliente.
Si te llevas una idea: usa los estados. Un factory sin estados solo ahorra escritura; uno con estados documenta qué significa cada situación de tu sistema.
En la siguiente lección veremos Tinker, para probar todo esto sin escribir una sola pantalla.
Saludos y éxitos.
Norvic Software
Desarrollamos el software que tu empresa necesita
Somos una fábrica de software en Bolivia. Construimos sistemas a medida y aplicaciones móviles, y llevamos Inteligencia Artificial a las empresas que ya tienen un sistema funcionando.
- Chatbots con Inteligencia Artificial
- Creación de agentes de IA
- Integración de IA en tus sistemas
- Desarrollo de software a medida
- Aplicaciones móviles iOS y Android
- Consultoría y asesoramiento técnico
Cotización sin costo · Respuesta directa por WhatsApp