data-seeding-fixtures-builder

安装量: 37
排名: #18902

安装

npx skills add https://github.com/patricio0312rev/skills --skill data-seeding-fixtures-builder

Data Seeding & Fixtures Builder

Generate realistic, deterministic seed data for development and testing.

Seed Data Strategy // prisma/seed.ts import { PrismaClient } from "@prisma/client"; import { faker } from "@faker-js/faker";

const prisma = new PrismaClient();

async function main() { console.log("🌱 Seeding database...");

// Clear existing data await prisma.order.deleteMany(); await prisma.user.deleteMany();

// Seed users const users = await seedUsers(10); console.log(✅ Created ${users.length} users);

// Seed orders const orders = await seedOrders(users, 50); console.log(✅ Created ${orders.length} orders);

console.log("✅ Seeding complete!"); }

main() .catch((e) => { console.error(e); process.exit(1); }) .finally(async () => { await prisma.$disconnect(); });

Factory Functions // factories/user.factory.ts import { faker } from "@faker-js/faker"; import { PrismaClient, User } from "@prisma/client";

const prisma = new PrismaClient();

export interface UserFactoryOptions { email?: string; name?: string; role?: "USER" | "ADMIN"; }

export class UserFactory { static async create(options: UserFactoryOptions = {}): Promise { return prisma.user.create({ data: { email: options.email || faker.internet.email(), name: options.name || faker.person.fullName(), role: options.role || "USER", createdAt: faker.date.past(), }, }); }

static async createMany( count: number, options: UserFactoryOptions = {} ): Promise { return Promise.all( Array.from({ length: count }, () => this.create(options)) ); }

static async createAdmin(): Promise { return this.create({ role: "ADMIN" }); } }

// Usage: // const user = await UserFactory.create(); // const admin = await UserFactory.createAdmin(); // const users = await UserFactory.createMany(10);

Realistic Fixtures // fixtures/products.ts import { Product } from "@prisma/client";

export const PRODUCT_FIXTURES: Omit< Product, "id" | "createdAt" | "updatedAt"

[] = [ { name: 'MacBook Pro 16"', description: "Powerful laptop for developers", price: 2499.99, stock: 50, category: "Electronics", }, { name: "iPhone 15 Pro", description: "Latest flagship smartphone", price: 999.99, stock: 100, category: "Electronics", }, { name: "AirPods Pro", description: "Wireless earbuds with noise cancellation", price: 249.99, stock: 200, category: "Electronics", }, ];

// Seed products async function seedProducts() { return Promise.all( PRODUCT_FIXTURES.map((product) => prisma.product.create({ data: product })) ); }

Deterministic Seeding // Use fixed seed for reproducibility import { faker } from "@faker-js/faker";

// Set seed for deterministic data faker.seed(12345);

// Same data every time const user1 = { email: faker.internet.email(), // Always same email name: faker.person.fullName(), // Always same name };

// Reset for different test faker.seed(67890);

Relationship Building // factories/order.factory.ts export class OrderFactory { static async create(userId: number): Promise { const products = await prisma.product.findMany({ take: 3 });

const order = await prisma.order.create({
  data: {
    userId,
    status: faker.helpers.arrayElement(["pending", "paid", "shipped"]),
    total: faker.number.float({ min: 10, max: 1000, precision: 0.01 }),
  },
});

// Create order items
await Promise.all(
  products.map((product) =>
    prisma.orderItem.create({
      data: {
        orderId: order.id,
        productId: product.id,
        quantity: faker.number.int({ min: 1, max: 5 }),
        price: product.price,
      },
    })
  )
);

return order;

}

static async createForUser(user: User, count: number): Promise { return Promise.all( Array.from({ length: count }, () => this.create(user.id)) ); } }

Environment-Specific Seeds // seeds/development.ts export async function seedDevelopment() { // Development: Few records, easy to debug const users = await UserFactory.createMany(5); const products = await ProductFactory.createMany(10);

for (const user of users) { await OrderFactory.createForUser(user, 2); } }

// seeds/staging.ts export async function seedStaging() { // Staging: Moderate data, realistic scenarios const users = await UserFactory.createMany(50); const products = await ProductFactory.createMany(100);

for (const user of users) { await OrderFactory.createForUser( user, faker.number.int({ min: 1, max: 10 }) ); } }

// seeds/testing.ts export async function seedTesting() { // Testing: Minimal, predictable data faker.seed(12345); // Deterministic

const user = await UserFactory.create({ email: "test@example.com", name: "Test User", });

const product = await ProductFactory.create({ name: "Test Product", price: 99.99, });

return { user, product }; }

// Main seed file async function main() { const env = process.env.NODE_ENV;

if (env === "development") { await seedDevelopment(); } else if (env === "staging") { await seedStaging(); } else if (env === "test") { await seedTesting(); } }

Database Reset Script // scripts/reset-db.ts import { PrismaClient } from "@prisma/client";

const prisma = new PrismaClient();

async function resetDatabase() { console.log("🗑️ Resetting database...");

// Disable foreign key checks (PostgreSQL) await prisma.$executeRawSET session_replication_role = 'replica';;

// Get all tables const tables = await prisma.$queryRaw<{ tablename: string }[]>SELECT tablename FROM pg_tables WHERE schemaname = 'public';;

// Truncate all tables for (const { tablename } of tables) { if (tablename !== "_prisma_migrations") { await prisma.$executeRawUnsafe(TRUNCATE TABLE "${tablename}" CASCADE;); console.log(Truncated ${tablename}); } }

// Re-enable foreign key checks await prisma.$executeRawSET session_replication_role = 'origin';;

console.log("✅ Database reset complete"); }

resetDatabase() .catch((e) => { console.error(e); process.exit(1); }) .finally(() => prisma.$disconnect());

Test Fixtures for E2E Tests // tests/fixtures/e2e.fixture.ts import { test as base } from "@playwright/test"; import { UserFactory, ProductFactory } from "../factories";

type Fixtures = { authenticatedUser: User; products: Product[]; };

export const test = base.extend({ authenticatedUser: async ({ page }, use) => { // Create user const user = await UserFactory.create();

// Login
await page.goto("/login");
await page.fill('[name="email"]', user.email);
await page.fill('[name="password"]', "password123");
await page.click('button[type="submit"]');

await use(user);

// Cleanup
await prisma.user.delete({ where: { id: user.id } });

},

products: async ({}, use) => { const products = await ProductFactory.createMany(5); await use(products);

// Cleanup
await prisma.product.deleteMany({
  where: { id: { in: products.map((p) => p.id) } },
});

}, });

// Usage: test("should add product to cart", async ({ authenticatedUser, products }) => { // Test with pre-seeded data });

Package.json Scripts { "scripts": { "db:seed": "tsx prisma/seed.ts", "db:seed:dev": "NODE_ENV=development tsx prisma/seed.ts", "db:seed:staging": "NODE_ENV=staging tsx prisma/seed.ts", "db:reset": "tsx scripts/reset-db.ts && npm run db:seed", "db:reset:test": "tsx scripts/reset-db.ts && NODE_ENV=test tsx prisma/seed.ts" } }

Best Practices Use factories: Reusable data generation Deterministic in tests: Fixed seed values Realistic fixtures: Production-like data Environment-specific: Different needs per environment Cleanup after tests: Avoid pollution Relationship integrity: Proper foreign keys Performance: Batch inserts for large datasets Output Checklist Seed script created Factory functions for each model Realistic fixtures defined Deterministic seeding (tests) Relationship building logic Environment-specific seeds Database reset script E2E test fixtures

返回排行榜