CI/CD
Front
Tests

How to Effectively Integrate Playwright with Prisma Using Seeding? 🎭🌱

6/2/2025
How to Effectively Integrate Playwright with Prisma Using Seeding? 🎭🌱

You're developing a great application with Prisma as your ORM and want to set up robust end-to-end tests with Playwright? Excellent choice! But wait... how do you test your application without data in your test database? That's where the magic of Prisma seeding comes in!

Let's discover together how to create a perfectly orchestrated test environment where your test data automatically generates every time your Playwright tests run.

πŸ€” The Challenge: Testing an Empty Application

Imagine the scene: you've developed a fantastic application with complex features, entity relationships, and sophisticated business workflows. You launch Playwright to test your user interface and... completely empty! No users, no products, no orders. How do you test a shopping cart without products? How do you verify a user profile without a user? 😱

This is exactly the problem we're going to solve today by combining the power of Prisma seeding with the efficiency of Playwright tests.

✨ The Solution: Prisma Seeding to the Rescue!

Prisma ORM offers a built-in seeding feature that allows you to systematically populate your database with consistent test data. This approach ensures that every test run starts from a known and predictable state.

Seeding with Prisma can be triggered in two ways:

  • Manually with prisma db seed
  • Automatically during prisma migrate reset or prisma migrate dev

πŸ› οΈ Configuration Step-by-Step

Step 1: Configure the Seed Script

First, let's add the seeding configuration to our package.json:

{
  "prisma": {
    "seed": "ts-node prisma/seed.ts"
  },
  "scripts": {
    "db:seed": "prisma db seed",
    "db:reset": "prisma migrate reset",
    "test:e2e": "playwright test",
    "test:e2e:setup": "npm run db:reset && npm run test:e2e"
  }
}

Step 2: Create a Robust Seed Script

Now, let's create our prisma/seed.ts file with realistic data:

import { PrismaClient } from '@prisma/client'
import { faker } from '@faker-js/faker'

const prisma = new PrismaClient()

async function main() {
  console.log('🌱 Starting seeding...')
  
  // Clean up existing database
  await prisma.order.deleteMany()
  await prisma.product.deleteMany()
  await prisma.user.deleteMany()
  
  // Create users
  const users = []
  for (let i = 0; i < 5; i++) {
    const user = await prisma.user.create({
      data: {
        name: faker.person.fullName(),
        email: faker.internet.email(),
        password: faker.internet.password(),
      },
    })
    users.push(user)
  }
  
  // Create products
  const products = []
  for (let i = 0; i < 10; i++) {
    const product = await prisma.product.create({
      data: {
        name: faker.commerce.productName(),
        description: faker.commerce.productDescription(),
        price: parseFloat(faker.commerce.price()),
        imageUrl: faker.image.imageUrl(),
      },
    })
    products.push(product)
  }
  
  // Create orders
  for (const user of users) {
    const numOrders = faker.number.int({ min: 0, max: 3 })
    for (let i = 0; i < numOrders; i++) {
      const orderItems = faker.helpers.arrayElements(products, faker.number.int({ min: 1, max: products.length }))
      
      const totalAmount = orderItems.reduce((sum, item) => sum + item.price, 0)
      
      await prisma.order.create({
        data: {
          user: {
            connect: { id: user.id },
          },
          items: {
            create: orderItems.map(item => ({
              product: {
                connect: { id: item.id },
              },
              quantity: faker.number.int({ min: 1, max: 3 }),
              price: item.price,
            })),
          },
          total: totalAmount,
          status: faker.helpers.arrayElement(['PENDING', 'COMPLETED', 'CANCELLED']),
        },
      })
    }
  }
  
  console.log('βœ… Seeding completed successfully!')
}

main()
  .then(async () => {
    await prisma.$disconnect()
  })
  .catch(async (e) => {
    console.error('❌ Error during seeding:', e)
    await prisma.$disconnect()
    process.exit(1)
  })

Step 3: Playwright Test Configuration

Now, let's configure Playwright to use our seeded database. In playwright.config.ts:

import { defineConfig, devices } from '@playwright/test'

export default defineConfig({
  testDir: './tests',
  
  // Crucial configuration: global setup
  globalSetup: require.resolve('./tests/global-setup.ts'),
  
  // Use a dedicated test database
  use: {
    baseURL: 'http://localhost:3000',
  },
  
  projects: [
    {
      name: 'chromium',
      use: { ...devices['Desktop Chrome'] },
    },
  ],
  
  // Development server for tests
  webServer: {
    command: 'npm run dev',
    url: 'http://localhost:3000',
    reuseExistingServer: !process.env.CI,
  },
})

Step 4: Global Setup Script

Let's create tests/global-setup.ts to orchestrate our test environment:

import { chromium, FullConfig } from '@playwright/test'
import { execSync } from 'child_process'

async function globalSetup(config: FullConfig) {
  console.log('πŸ”„ Preparing the test environment...')
  
  // Reset and seed the database
  execSync('npm run db:reset -- --force', { stdio: 'inherit' })
  
  console.log('βœ… Database ready for tests!')
}

export default globalSetup

🎯 Writing Effective Playwright Tests

With our setup in place, our tests become more predictable and robust: import test, expect from '@playwright/test'

test.describe('E-commerce Flow', () => {
  test('user can browse and purchase products', async ({ page }) => {
    await page.goto('/')
    
    // Verify that products are present (thanks to seeding!)
    await expect(page.locator('[data-testid="product-card"]')).toHaveCount(10)
    
    // Click on the first product
    await page.locator('[data-testid="product-card"]').first().click()
    
    // Add to cart
    await page.click('[data-testid="add-to-cart"]')
    
    // Verify the cart
    await page.goto('/cart')
    await expect(page.locator('[data-testid="cart-item"]')).toHaveCount(1)
    
    // Proceed to checkout
    await page.click('[data-testid="checkout-button"]')
    await expect(page).toHaveURL(/.*checkout/)
  })
  
  test('user can view their profile with their orders', async ({ page }) => {
    await page.goto('/profile')
    
    // Thanks to seeding, we know there are orders
    await expect(page.locator('[data-testid="order-history"]')).toBeVisible()
    await expect(page.locator('[data-testid="order-item"]')).toHaveCount.toBeGreaterThan(0)
  })
})

πŸš€ CI/CD Integration with GitHub Actions

For full integration into your pipeline, here's an example GitHub action:

name: E2E Tests
on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    
    services:
      postgres:
        image: postgres:13
        env:
          POSTGRES_PASSWORD: test
          POSTGRES_DB: testdb
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5
    
    steps:
      - uses: actions/checkout@v3
      
      - name: Setup Node.js
        uses: actions/setup-node@v3
        with:
          node-version: '18'
          cache: 'npm'
      
      - name: Install dependencies
        run: npm ci
      
      - name: Setup database
        run: |
          npm run db:migrate
          npm run db:seed
        env:
          DATABASE_URL: postgresql://postgres:test@localhost:5432/testdb
      
      - name: Install Playwright
        run: npx playwright install
      
      - name: Run tests
        run: npm run test:e2e
        env:
          DATABASE_URL: postgresql://postgres:test@localhost:5432/testdb

🌟 The Benefits of this Approach

This Prisma + Playwright + Seeding integration strategy brings you:

πŸ”„ Consistency: Every test starts from the same state, eliminating false positives/negatives

⚑ Speed: No need for tedious manual setup, everything is automated

🎯 Realism: Your tests use data that resembles reality thanks to Faker.js

πŸ”’ Isolation: Each test run is isolated and predictable

πŸ’° Savings: Drastic reduction in test maintenance time

With this configuration, your Playwright tests become a true safety net for your application, allowing you to deploy with confidence!

So, ready to transform your tests into a well-oiled machine? πŸš€

How to Effectively Integrate Playwright with Prisma Using Seeding? 🎭🌱 | Blog & News