Factories
Factories
Factories generate realistic test data for your database models. They integrate with Drizzle schema and respect your column types and constraints.
Import
import { defineFactory, createSeeder } from '@loewen-digital/fullstack/testing'Defining a factory
import { defineFactory } from '@loewen-digital/fullstack/testing'import { users, posts } from '../db/schema.js'
const userFactory = defineFactory(users, { name: () => 'Alice Example', email: (i) => `user${i}@example.com`, passwordHash: () => '$argon2id$...', createdAt: () => new Date(), active: () => true,})
const postFactory = defineFactory(posts, { title: (i) => `Post number ${i}`, body: () => 'Lorem ipsum dolor sit amet...', status: () => 'draft', authorId: () => 1, publishedAt: () => null,})Creating records in tests
import { describe, it, expect, beforeEach } from 'vitest'import { createTestStack } from '@loewen-digital/fullstack/testing'
describe('post listing', () => { let stack: ReturnType<typeof createTestStack>
beforeEach(() => { stack = createTestStack() })
it('returns only published posts', async () => { // Create specific records await userFactory.create(stack.db, { name: 'Alice' }) await postFactory.create(stack.db, { status: 'published' }) await postFactory.create(stack.db, { status: 'draft' })
const published = await stack.db.query.posts.findMany({ where: (p, { eq }) => eq(p.status, 'published'), })
expect(published).toHaveLength(1) })})Creating multiple records
// Create 5 usersconst users = await userFactory.createMany(db, 5)
// Create 3 posts for a specific userconst posts = await postFactory.createMany(db, 3, { authorId: user.id })Overriding attributes
Pass any attribute to override the factory default:
const admin = await userFactory.create(db, { email: 'admin@example.com', role: 'admin',})States
Define named states for common variations:
const postFactory = defineFactory(posts, { title: (i) => `Post ${i}`, status: () => 'draft',}).state('published', { status: 'published', publishedAt: () => new Date(),}).state('archived', { status: 'archived',})
// Use a stateconst publishedPost = await postFactory.published().create(db)const archivedPost = await postFactory.archived().create(db)Building without persisting
// Returns a plain object without inserting into the databaseconst userData = userFactory.build({ name: 'Bob' })Seeder integration
Compose factories into seeders for development data:
import { createSeeder } from '@loewen-digital/fullstack/testing'
export const seed = createSeeder(async (db) => { const alice = await userFactory.create(db, { email: 'alice@example.com' }) await postFactory.createMany(db, 5, { authorId: alice.id, status: 'published' })})npx fullstack db:seed