codeception/module-datafactory
Codeception DataFactory module for generating realistic test data and fixtures in your suites. Helps create models/entities with sensible defaults and overrides, making tests cleaner, faster to write, and easier to maintain across unit, functional, and acceptance tests.
Installation
Add the module to your composer.json:
composer require --dev codeception/module-datafactory
Register it in codeception.yml under modules:
modules:
enabled:
- DataFactory
First Use Case
Define a factory in tests/_data/factories.php:
$I->haveInDatabase('users', [
'name' => 'John Doe',
'email' => 'john@example.com',
]);
Or use the DataFactory DSL:
$I->haveInDatabase('users', [
'name' => 'John Doe',
'email' => 'john@example.com',
]);
Where to Look First
tests/_data/factories.php in your project.haveInDatabase(), haveInMemory(), and haveInFile().Database Factories
$I->haveInDatabase('posts', [
'title' => 'Test Post',
'body' => 'Test content',
'user_id' => function() {
return $I->grabFromDatabase('users', 'id', ['name' => 'John Doe']);
}
]);
Memory Factories (for non-database objects)
$I->haveInMemory('Order', [
'id' => 1,
'items' => [
['product_id' => 1, 'quantity' => 2],
],
]);
File Factories (for JSON/XML/CSV)
$I->haveInFile('tests/_data/users.json', [
'name' => 'Jane Doe',
'email' => 'jane@example.com',
]);
Reusable Factories
Define a factory in _data/factories.php:
$I->haveInDatabase('users', [
'name' => 'Admin',
'email' => 'admin@example.com',
'role' => 'admin',
]);
Reference it in tests:
$I->haveInDatabase('posts', [
'user_id' => function() {
return $I->grabFromDatabase('users', 'id', ['role' => 'admin']);
},
]);
Dynamic Data with Callbacks
$I->haveInDatabase('products', [
'name' => 'Dynamic Product',
'price' => function() {
return rand(10, 100);
},
]);
haveInDatabase() with Eloquent models.
$I->haveInDatabase('App\Models\User', [
'name' => 'Test User',
'email' => 'test@example.com',
]);
DatabaseSeeder for complex setups.haveInFile() for large datasets (e.g., JSON fixtures).Database Transactions
Ensure your test database is fresh (e.g., using Db module or Laravel’s DatabaseTransactions trait). Factories persist data unless rolled back.
Foreign Key Constraints
Insert parent records (e.g., users) before child records (e.g., posts). Use callbacks to reference IDs dynamically:
$I->haveInDatabase('users', ['name' => 'Parent']);
$I->haveInDatabase('posts', [
'user_id' => function() {
return $I->grabFromDatabase('users', 'id', ['name' => 'Parent']);
},
]);
File Paths
Ensure haveInFile() paths are correct (relative to tests/ by default). Use absolute paths if needed:
$I->haveInFile(__DIR__ . '/_data/users.json', [...]);
Memory Leaks
Avoid mixing haveInMemory() with database factories unless explicitly needed (e.g., for API tests).
grabFromDatabase() or seeInDatabase() to check inserted data.
$I->seeInDatabase('users', ['email' => 'john@example.com']);
-vv) to debug factory execution.Custom Factories Extend the module by creating a custom factory class:
class CustomFactory extends \Codeception\Module\DataFactory
{
public function haveCustomData($data) { ... }
}
Register it in codeception.yml:
modules:
config:
DataFactory:
class: CustomFactory
Hooks
Use Codeception’s before/after hooks to reset data:
$I->haveInDatabase('users', ['name' => 'Cleanup']);
$I->runSql('DELETE FROM users WHERE name = ?', ['Cleanup']);
Laravel Integration
For Laravel, pair with Db module to reset the database:
$I->haveInDatabase('users', [...]);
$I->runSql('SET FOREIGN_KEY_CHECKS = 0; TRUNCATE users;');
haveInDatabase() for Eloquent: Simplifies model interactions.Filesystem module to manage fixture files.test_user_) to avoid conflicts.How can I help you explore Laravel packages today?