league/factory-muffin
Factory Muffin helps you rapidly create test objects in PHP with simple, reusable model factories (inspired by factory_girl). Define models and generate instances for fixtures and unit tests; optional Faker integration is available via factory-muffin-faker.
Installation:
composer require league/factory-muffin --dev
Add to composer.json under require-dev to ensure it’s only installed in testing environments.
Basic Usage:
use League\FactoryMuffin\Factory;
use League\FactoryMuffin\FakerProvider;
// Define a factory for a User model
$factory = new Factory();
$factory->for(User::class)
->create(function (FakerProvider $faker) {
return new User([
'name' => $faker->name,
'email' => $faker->unique()->safeEmail,
]);
});
// Generate a user
$user = $factory->create(User::class);
First Use Case:
Use FactoryMuffin to generate test data for unit/integration tests. Replace hardcoded test data with dynamic, realistic objects.
Factory Definition:
UserFactory, PostFactory) in a dedicated Factories directory.trait BaseFactory {
protected function commonAttributes(FakerProvider $faker) {
return ['created_at' => $faker->dateTimeThisYear];
}
}
Relationships:
$factory->for(Post::class)
->create(function (FakerProvider $faker) {
return new Post([
'title' => $faker->sentence,
'user_id' => $factory->create(User::class)->id,
]);
});
State Management:
state() to define reusable configurations:
$factory->for(User::class)
->state('admin', function (FakerProvider $faker) {
return ['role' => 'admin'];
});
$admin = $factory->state(User::class, 'admin');
Testing Integration:
phpunit.xml or a TestCase bootstrap:
class TestCase extends \Tests\TestCase {
protected $factory;
protected function setUp(): void {
$this->factory = new Factory();
// Register factories here
}
}
Laravel Synergy:
Combine with Laravel’s DatabaseTransactions trait for seamless test rollbacks:
use Illuminate\Foundation\Testing\DatabaseTransactions;
class UserTest extends TestCase {
use DatabaseTransactions;
public function test_user_creation() {
$user = $this->factory->create(User::class);
// Assertions...
}
}
Faker Customization:
Extend FakerProvider for domain-specific data:
$factory->for(User::class)
->create(function (FakerProvider $faker) {
$faker->addProvider(new CustomFakerProvider());
return new User(['email' => $faker->customEmail]);
});
State Clashes:
$factory->state(User::class, 'admin')->state(User::class, 'admin'); // Silent failure
Circular Dependencies:
User needs Post, Post needs User) cause infinite loops. Use lazy loading or mocks for circular relationships.Faker Seed Collisions:
unique()->email) may fail if the same seed is reused. Use Faker::unique(true) or reset the seed per test.Static Analysis:
/**
* @return User
*/
public function createUser() { ... }
Verify Factory Registration: Check if factories are registered before use:
if (!$factory->has(User::class)) {
throw new \RuntimeException('User factory not registered!');
}
Inspect Generated Data:
Use var_dump() or dd() to debug factory outputs:
$user = $factory->create(User::class);
dd($user->toArray());
Clear States: Reset states between tests to avoid pollution:
$factory->clearStates(User::class);
Custom Providers:
Extend FakerProvider for domain-specific data:
class AppFakerProvider extends FakerProvider {
public function password() {
return $this->faker->password(8, '!@#$%^&*');
}
}
After Creation Hooks:
Use afterCreate() to modify objects post-creation:
$factory->for(User::class)
->afterCreate(function (User $user) {
$user->update(['last_login' => now()]);
});
Factory Inheritance: Extend base factories for shared logic:
class BaseModelFactory {
public function createWithTimestamps(FakerProvider $faker) {
return ['created_at' => $faker->dateTimeThisYear];
}
}
How can I help you explore Laravel packages today?