Custom Fixtures

Back

Loading concept...

🎭 Playwright Custom Fixtures: Your Personal Stage Crew

Imagine you’re putting on a play. Before the actors perform, the stage crew sets up everything—lights, props, costumes. After the show, they clean it all up. Custom fixtures in Playwright work exactly like your personal stage crew!


🌟 What Are Custom Fixtures?

Think of fixtures like helpful friends who prepare things before you need them and clean up after you’re done.

Real Life Example:

  • Before a birthday party → Your mom sets up decorations 🎈
  • During the party → You have fun!
  • After the party → Mom cleans everything up

In Playwright:

  • Before a test → Fixture prepares what you need
  • During test → You use it!
  • After test → Fixture cleans up automatically

🔧 Creating Custom Fixtures

Creating a fixture is like teaching a helper what to prepare for you.

// fixtures.js
import { test as base } from '@playwright/test';

// Create a custom fixture
export const test = base.extend({
  // 'todoPage' is our helper's name
  todoPage: async ({ page }, use) => {
    // BEFORE: Go to the todo app
    await page.goto('https://demo.com/todos');

    // DURING: Hand it to the test
    await use(page);

    // AFTER: Clean up (optional)
    await page.close();
  }
});

What’s happening?

  1. async ({ page }, use) → Takes what it needs (page)
  2. Code before use → Sets things up
  3. await use(page) → Gives it to your test
  4. Code after use → Cleans up

📦 The test.extend Method

test.extend is like a magic box that creates new helpers from existing ones.

graph TD A["Base Test"] --> B["test.extend"] B --> C["Your Custom Test"] C --> D["Has All Original Powers"] C --> E["Plus Your New Fixtures!"]

Simple Example:

import { test as base } from '@playwright/test';

export const test = base.extend({
  // Add a logged-in user helper
  loggedInPage: async ({ page }, use) => {
    await page.goto('/login');
    await page.fill('#email', 'test@test.com');
    await page.fill('#password', 'secret');
    await page.click('button[type=submit]');
    await use(page);
  }
});

Now every test can start already logged in! 🎉


⏰ Fixture Scopes: How Long Do Helpers Stay?

Fixtures can stay for different amounts of time. It’s like hiring helpers:

Scope Like Hiring… Example
test A helper for ONE task Fresh page per test
worker A helper for the whole day Database connection
export const test = base.extend({
  // Scope: 'test' (default)
  // New page for EACH test
  freshPage: async ({ page }, use) => {
    await use(page);
  },

  // Scope: 'worker'
  // Same database for ALL tests
  database: [async ({}, use) => {
    const db = await connectDB();
    await use(db);
    await db.close();
  }, { scope: 'worker' }]
});

When to use which?

  • test scope → When each test needs a clean start
  • worker scope → For expensive things (databases, servers)

🤖 Auto-use Fixtures: Always-On Helpers

Some helpers should always be there, like a night light that turns on automatically when it’s dark.

export const test = base.extend({
  // This runs for EVERY test automatically!
  autoScreenshot: [async ({ page }, use) => {
    await use();
    // Take screenshot after every test
    await page.screenshot({
      path: `screenshots/${Date.now()}.png`
    });
  }, { auto: true }]  // ← Magic switch!
});

Common auto-use examples:

  • 📸 Take screenshots after tests
  • 📝 Log test start/end times
  • 🔧 Set up common settings

🔗 Fixture Dependencies: Helpers That Need Other Helpers

Sometimes one helper needs another helper first. It’s like a chain of helpers!

graph TD A["browser"] --> B["context"] B --> C["page"] C --> D["loggedInPage"] D --> E["dashboardPage"]
export const test = base.extend({
  // Helper 1: Logged in user
  userPage: async ({ page }, use) => {
    await page.goto('/login');
    await page.fill('#email', 'user@test.com');
    await page.click('#login-btn');
    await use(page);
  },

  // Helper 2: NEEDS userPage first!
  dashboardPage: async ({ userPage }, use) => {
    // userPage is already logged in
    await userPage.goto('/dashboard');
    await use(userPage);
  }
});

The magic: Playwright figures out the order automatically! 🪄


🧹 Fixture Teardown: The Cleanup Crew

Everything after await use() is teardown. It’s like the cleanup after a party.

export const test = base.extend({
  tempFile: async ({}, use) => {
    // SETUP: Create a temporary file
    const file = await createTempFile();
    console.log('📁 Created temp file');

    // HAND OVER to test
    await use(file);

    // TEARDOWN: Delete the file
    await deleteTempFile(file);
    console.log('🗑️ Cleaned up temp file');
  }
});

Important rules:

  • Teardown runs even if test FAILS ✅
  • Teardown runs in REVERSE order 🔄
  • Always clean up what you create! 🧹

🎯 Complete Example: Putting It All Together

// fixtures.js
import { test as base } from '@playwright/test';

export const test = base.extend({
  // Auto-use: Timer for every test
  testTimer: [async ({}, use) => {
    const start = Date.now();
    await use();
    const time = Date.now() - start;
    console.log(`⏱️ Test took ${time}ms`);
  }, { auto: true }],

  // Worker scope: Expensive database
  db: [async ({}, use) => {
    const db = await connectDatabase();
    await use(db);
    await db.disconnect();
  }, { scope: 'worker' }],

  // Depends on page: Logged in user
  authPage: async ({ page }, use) => {
    await page.goto('/login');
    await page.fill('#user', 'admin');
    await page.fill('#pass', '12345');
    await page.click('#submit');
    await use(page);
  },

  // Depends on authPage: Admin dashboard
  adminDashboard: async ({ authPage }, use) => {
    await authPage.goto('/admin');
    await use(authPage);
  }
});

// In your test file:
test('admin can see users', async ({ adminDashboard, db }) => {
  // adminDashboard is ready and logged in!
  // db is connected and shared across tests!
  await expect(adminDashboard.locator('h1'))
    .toHaveText('Admin Panel');
});

🚀 Quick Summary

Concept What It Does Think Of It As…
Custom Fixture Prepares stuff before tests Stage crew
test.extend Creates your custom test Magic box
Scope: test Fresh for each test Daily helper
Scope: worker Shared across tests Full-time helper
Auto-use Runs automatically Night light
Dependencies One fixture needs another Chain of helpers
Teardown Cleans up after Cleanup crew

💡 Remember This!

Fixtures are like setting up a playdate:

  1. Mom prepares snacks (Setup)
  2. Kids play (Your test runs)
  3. Mom cleans up (Teardown)

You just focus on playing! The rest happens automatically. 🎮

You’ve got this! Custom fixtures make your tests cleaner, faster, and easier to write. Now go build something amazing! 🌟

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.