install
source · Clone the upstream repo
git clone https://github.com/Intense-Visions/harness-engineering
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/Intense-Visions/harness-engineering "$T" && mkdir -p ~/.claude/skills && cp -r "$T/agents/skills/codex/test-factory-patterns" ~/.claude/skills/intense-visions-harness-engineering-test-factory-patterns-a0e26c && rm -rf "$T"
manifest:
agents/skills/codex/test-factory-patterns/SKILL.mdsource content
Test Factory Patterns
Build maintainable test data using factory functions, builders, and faker.js
When to Use
- Creating test data for unit and integration tests
- Reducing duplication in test setup across test files
- Generating realistic fake data for testing edge cases
- Building complex object graphs with sensible defaults
Instructions
- Simple factory function with defaults and overrides:
function createUser(overrides?: Partial<User>): User { return { id: crypto.randomUUID(), name: 'Test User', email: `user-${crypto.randomUUID().slice(0, 8)}@test.com`, role: 'user', createdAt: new Date(), ...overrides, }; } // Usage const admin = createUser({ role: 'admin', name: 'Admin' }); const alice = createUser({ name: 'Alice' });
- Factory with faker.js for realistic data:
import { faker } from '@faker-js/faker'; function createUser(overrides?: Partial<User>): User { return { id: faker.string.uuid(), name: faker.person.fullName(), email: faker.internet.email(), role: 'user', avatar: faker.image.avatar(), createdAt: faker.date.past(), ...overrides, }; }
- Builder pattern for complex objects:
class UserBuilder { private data: Partial<User> = {}; withName(name: string): this { this.data.name = name; return this; } withRole(role: Role): this { this.data.role = role; return this; } withEmail(email: string): this { this.data.email = email; return this; } asAdmin(): this { this.data.role = 'admin'; return this; } build(): User { return createUser(this.data); } } // Usage const admin = new UserBuilder().withName('Alice').asAdmin().build();
- Factory for related entities:
function createPost( overrides?: Partial<Post> & { author?: Partial<User> } ): Post & { author: User } { const author = createUser(overrides?.author); return { id: crypto.randomUUID(), title: faker.lorem.sentence(), content: faker.lorem.paragraphs(3), authorId: author.id, published: false, createdAt: new Date(), ...overrides, author, }; }
- Batch factory for lists:
function createUsers(count: number, overrides?: Partial<User>): User[] { return Array.from({ length: count }, () => createUser(overrides)); } const tenAdmins = createUsers(10, { role: 'admin' });
- Deterministic factories for snapshot testing:
faker.seed(42); // Same seed = same data every run function createDeterministicUser(): User { return createUser(); // Always generates the same sequence }
- Database factory for integration tests:
async function createDbUser(prisma: PrismaClient, overrides?: Partial<User>) { const data = createUser(overrides); return prisma.user.create({ data: { email: data.email, name: data.name, role: data.role, }, }); } // In tests const user = await createDbUser(prisma, { role: 'admin' });
- Organize factories in a central file:
// test/factories/index.ts export { createUser, createUsers, UserBuilder } from './user-factory'; export { createPost, createPosts } from './post-factory'; export { createOrder } from './order-factory';
Details
Test factories centralize test data creation, providing consistent defaults while allowing per-test customization. They eliminate the "copy-paste test setup" anti-pattern.
Factory vs fixture vs seed:
- Factory — creates data programmatically with overrides. Best for unit and integration tests
- Fixture — static data loaded from files (JSON, SQL). Best for large, stable datasets
- Seed — populates a database for development. Factories can be used inside seeds
Faker.js tips:
makes all subsequent calls deterministic — use for reproducible testsfaker.seed(n)
picks randomly from a listfaker.helpers.arrayElement(['a', 'b', 'c'])
generates multiple valuesfaker.helpers.multiple(fn, { count: 5 })
Factory design principles:
- Default values should produce a valid object (passes all validation)
- Override only what matters for the test — defaults handle the rest
- Unique values (email, ID) should be generated, not hardcoded — prevents constraint violations
- Related objects should be creatable together (post + author)
Trade-offs:
- Factories reduce test setup duplication — but add a maintenance surface (factory changes can break many tests)
- Faker generates realistic data — but can produce edge cases unexpectedly (very long names, unicode characters)
- Builders are expressive — but verbose for simple objects. Use plain factories for objects with fewer than 5 fields
- Deterministic seeds help snapshots — but hide randomized testing benefits
Source
Process
- Read the instructions and examples in this document.
- Apply the patterns to your implementation, adapting to your specific context.
- Verify your implementation against the details and edge cases listed above.
Harness Integration
- Type: knowledge — this skill is a reference document, not a procedural workflow.
- No tools or state — consumed as context by other skills and agents.
Success Criteria
- The patterns described in this document are applied correctly in the implementation.
- Edge cases and anti-patterns listed in this document are avoided.