patterntypescriptnoneTip
Fixtures vs Factories: Test Data Strategies
Viewed 0 times
factoryfixturetest databuilder patternfactory-girltest isolation
Problem
Static test fixtures become stale and hard to maintain. Tests that share fixture objects fail when one test mutates the shared data. Large fixture files obscure what a test actually cares about.
Solution
Use factory functions with sensible defaults that tests can override minimally.
// Factory pattern with partial overrides
function createUser(overrides: Partial<User> = {}): User {
return {
id: 'usr_test_' + Math.random().toString(36).slice(2),
name: 'Test User',
email: 'test@example.com',
role: 'user',
createdAt: new Date('2024-01-01'),
...overrides,
};
}
// Test only specifies what matters
test('admin can delete users', () => {
const admin = createUser({ role: 'admin' });
const target = createUser();
expect(canDelete(admin, target)).toBe(true);
});
// For DB-backed tests, use factory-boy or similar
import { factory } from 'factory-girl';
factory.define('user', UserModel, {
name: factory.sequence('user.name', (n: number) => `User ${n}`),
email: factory.sequence('user.email', (n: number) => `user${n}@test.com`),
});
const user = await factory.create('user', { role: 'admin' });Why
Factories express test intent (only the fields that matter), produce isolated data per test (no shared references), and are easy to maintain when the model changes.
Gotchas
- Use unique IDs in factories to avoid database unique constraint violations when creating multiple instances.
- Factory functions should be pure — don't use global counters without resetting between tests.
- For large object graphs, build nested factories to avoid deeply nested manual construction.
Revisions (0)
No revisions yet.