Test Data and Environment

Back

Loading concept...

🎭 Playwright: Test Data & Environment

The Recipe Analogy 🍳

Imagine you’re a chef cooking the same dish in three different kitchens—your home, a friend’s place, and a fancy restaurant.

Each kitchen has:

  • Different ingredients (test data)
  • Different ovens and tools (environment variables)
  • Different portion sizes (parameterized tests)

But you want the same delicious result every time!

That’s exactly what Playwright’s test data and environment configuration does. It lets your tests “cook” perfectly in ANY kitchen!


🌿 Environment Variables

What Are They?

Think of environment variables like secret notes you stick on your refrigerator.

📌 Note on fridge: "Sugar jar is in the TOP cabinet"

Your tests read these notes to know where things are and how to behave.

Real Example

You have a website. In development, it’s at localhost:3000. In production, it’s at myapp.com.

Without environment variables:

// ❌ Hard to change!
await page.goto('http://localhost:3000');

With environment variables:

// ✅ Reads from your "note"!
await page.goto(process.env.BASE_URL);

How to Set Them

Method 1: In your terminal

BASE_URL=http://localhost:3000 npx playwright test

Method 2: Create a .env file

BASE_URL=http://localhost:3000
API_KEY=my-secret-key
TEST_USER=alice@test.com

Method 3: In playwright.config.ts

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

🎯 Pro Tip

Never put real passwords in your code! Use environment variables:

// ✅ Safe!
const password = process.env.TEST_PASSWORD;

// ❌ Never do this!
const password = 'my-actual-password-123';

📦 Test Data Management

The Toy Box Approach

Imagine your tests are kids who need toys to play.

Bad approach: Each kid brings random toys. Chaos!

Good approach: You have a organized toy box where everyone knows what’s inside.

Creating a Test Data File

Make a file called testData.ts:

export const users = {
  admin: {
    email: 'admin@test.com',
    password: 'AdminPass123',
    role: 'administrator'
  },
  regular: {
    email: 'user@test.com',
    password: 'UserPass456',
    role: 'user'
  }
};

export const products = [
  { name: 'Red Ball', price: 10 },
  { name: 'Blue Car', price: 25 },
];

Using Test Data in Tests

import { users, products } from './testData';

test('admin can see all users', async ({ page }) => {
  // Use data from your "toy box"
  await page.fill('#email', users.admin.email);
  await page.fill('#password', users.admin.password);
  await page.click('button[type="submit"]');

  // Now logged in as admin!
});

📊 Data Flow Diagram

graph TD A["testData.ts"] --> B["Test File 1"] A --> C["Test File 2"] A --> D["Test File 3"] B --> E["Same Data Everywhere!"] C --> E D --> E

Fixtures: Shared Setup

Playwright has fixtures—like a helpful assistant who prepares everything before each test.

// fixtures.ts
import { test as base } from '@playwright/test';
import { users } from './testData';

export const test = base.extend({
  loggedInPage: async ({ page }, use) => {
    // Setup: Log in before test
    await page.goto('/login');
    await page.fill('#email', users.regular.email);
    await page.fill('#password', users.regular.password);
    await page.click('button[type="submit"]');

    // Give the logged-in page to the test
    await use(page);

    // Cleanup: Log out after test
    await page.click('#logout');
  },
});

Now your tests get a pre-logged-in page:

import { test } from './fixtures';

test('user sees dashboard', async ({ loggedInPage }) => {
  // Already logged in! 🎉
  await expect(loggedInPage).toHaveURL('/dashboard');
});

🔄 Parameterized Tests

The Cookie Cutter Concept

You want to make cookies in 5 different shapes using the same dough.

Instead of writing 5 separate recipes, you use ONE recipe with different cookie cutters.

Basic Parameterization

const browsers = ['chromium', 'firefox', 'webkit'];

for (const browser of browsers) {
  test(`works on ${browser}`, async ({ page }) => {
    await page.goto('/');
    await expect(page).toHaveTitle('My App');
  });
}

This creates 3 tests from ONE code block!

Testing Multiple Users

const testUsers = [
  { name: 'Alice', email: 'alice@test.com' },
  { name: 'Bob', email: 'bob@test.com' },
  { name: 'Charlie', email: 'charlie@test.com' },
];

for (const user of testUsers) {
  test(`${user.name} can sign up`, async ({ page }) => {
    await page.goto('/signup');
    await page.fill('#name', user.name);
    await page.fill('#email', user.email);
    await page.click('#submit');

    await expect(page.locator('.welcome'))
      .toContainText(`Hello, ${user.name}!`);
  });
}

Using test.describe for Organization

const screenSizes = [
  { name: 'Mobile', width: 375, height: 667 },
  { name: 'Tablet', width: 768, height: 1024 },
  { name: 'Desktop', width: 1920, height: 1080 },
];

for (const size of screenSizes) {
  test.describe(`${size.name} view`, () => {
    test.use({
      viewport: {
        width: size.width,
        height: size.height
      }
    });

    test('menu is visible', async ({ page }) => {
      await page.goto('/');
      await expect(page.locator('nav'))
        .toBeVisible();
    });
  });
}

🎯 Real-World Pattern

Testing a form with valid and invalid data:

const formTests = [
  {
    scenario: 'valid email',
    email: 'good@email.com',
    shouldPass: true
  },
  {
    scenario: 'missing @',
    email: 'bademail.com',
    shouldPass: false
  },
  {
    scenario: 'empty email',
    email: '',
    shouldPass: false
  },
];

for (const { scenario, email, shouldPass } of formTests) {
  test(`form with ${scenario}`, async ({ page }) => {
    await page.goto('/contact');
    await page.fill('#email', email);
    await page.click('#submit');

    if (shouldPass) {
      await expect(page.locator('.success'))
        .toBeVisible();
    } else {
      await expect(page.locator('.error'))
        .toBeVisible();
    }
  });
}

🏆 Putting It All Together

Here’s how everything works together:

graph TD A[".env file"] --> B["Environment Variables"] B --> C["playwright.config.ts"] D["testData.ts"] --> E["Test Files"] C --> E E --> F["Parameterized Tests"] F --> G["Run on Chrome"] F --> H["Run on Firefox"] F --> I["Run on Safari"]

Complete Example

.env

BASE_URL=http://localhost:3000
TEST_PASSWORD=SecretPass123

testData.ts

export const users = [
  { name: 'Alice', email: 'alice@test.com' },
  { name: 'Bob', email: 'bob@test.com' },
];

playwright.config.ts

import { defineConfig } from '@playwright/test';

export default defineConfig({
  use: {
    baseURL: process.env.BASE_URL,
  },
});

login.spec.ts

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

for (const user of users) {
  test(`${user.name} can log in`, async ({ page }) => {
    await page.goto('/login');
    await page.fill('#email', user.email);
    await page.fill('#password', process.env.TEST_PASSWORD!);
    await page.click('#submit');

    await expect(page.locator('.welcome'))
      .toContainText(user.name);
  });
}

🎉 You Did It!

You now understand:

Concept What It Does
Environment Variables Store secrets & settings outside code
Test Data Management Organize reusable test data
Parameterized Tests Run same test with different inputs

Your tests can now run anywhere, with any data, using any configuration—just like a master chef cooking in any kitchen! 👨‍🍳


📌 Quick Reference

// Environment variable
const url = process.env.BASE_URL;

// Test data import
import { users } from './testData';

// Parameterized test
for (const user of users) {
  test(`test for ${user.name}`, async ({ page }) => {
    // test code here
  });
}

Remember: Keep your tests flexible, your data organized, and your secrets safe! 🔐

Loading story...

Story - Premium Content

Please sign in to view this story and start learning.

Upgrade to Premium to unlock full access to all stories.

Stay Tuned!

Story is coming soon.

Story Preview

Story - Premium Content

Please sign in to view this concept and start learning.

Upgrade to Premium to unlock full access to all content.