SoftwareTestPilot
Module 07 · Lab 1
Intermediate
11 min read

Data-Driven Testing in Playwright — Parameterize Like a Pro

Run the same test against hundreds of inputs using parameterized tests, JSON fixtures, and CSV imports — and stop copy-pasting spec files.

1. Parameterized tests

The simplest form of data-driven testing: a plain for loop around test(). Playwright registers one independent test per iteration.

import { test, expect } from '@playwright/test';

const users = [
  { name: 'Alice', role: 'admin' },
  { name: 'Bob',   role: 'viewer' },
];

for (const user of users) {
  test(`dashboard shows correct role for ${user.name}`, async ({ page }) => {
    await page.goto('/');
    await expect(page.getByText(user.role)).toBeVisible();
  });
}

2. JSON fixtures

Keep your data out of the spec. JSON is the fastest path — TypeScript loads it natively.

import users from './fixtures/users.json' assert { type: 'json' };

for (const user of users) {
  test(`login as ${user.email}`, async ({ page }) => {
    // ...
  });
}

3. CSV parsing

Use csv-parse when data comes from product/business teams — they live in spreadsheets.

import fs from 'node:fs';
import { parse } from 'csv-parse/sync';

const records = parse(fs.readFileSync('fixtures/users.csv'), {
  columns: true,
  skip_empty_lines: true,
});

for (const row of records) {
  test(`signup ${row.email}`, async ({ page }) => { /* ... */ });
}

4. Environment variables

Use .env + dotenv for per-environment switches (staging vs prod, feature flags, secrets). Read them via process.env.

// playwright.config.ts
import 'dotenv/config';

export default defineConfig({
  use: { baseURL: process.env.BASE_URL ?? 'http://localhost:3000' },
});

5. Test data builders

Factory functions generate unique data per run, so tests don't collide on duplicates (e.g. "email already in use").

import { randomUUID } from 'node:crypto';

export const makeUser = (overrides: Partial<User> = {}): User => ({
  email: `qa+${randomUUID()}@example.com`,
  password: 'Passw0rd!',
  ...overrides,
});

// usage
const user = makeUser({ role: 'admin' });

6. Hands-on task

Create a users.csv with 50 rows. Loop over it and run a login test per row, asserting the correct landing page based on each user's role.

for (const row of records) {
  test(`login ${row.email} lands on ${row.expected_page}`, async ({ page }) => {
    await loginAs(page, row.email, row.password);
    await expect(page).toHaveURL(new RegExp(row.expected_page));
  });
}

7. What's next

Next, move into Module 08: CI/CD with GitHub Actions to run your suite on every push and ship reports your team will actually read.

Up next in the learning path

Module 08: CI/CD with GitHub Actions