</>

Technology

Playwright

Difficulty

Intermediate

Interview Question

What are fixtures in Playwright and how do you create custom fixtures?

Answer

Playwright Fixtures

Fixtures are reusable setup/teardown functions that Playwright injects into tests. Built-in fixtures include page, browser, context, request, and browserName.

Built-in Fixtures

TypeScript
test('uses built-in fixtures', async ({
  page,       // A new Page in a new BrowserContext
  browser,    // The Browser instance
  context,    // The BrowserContext
  request,    // API request context
  browserName // 'chromium' | 'firefox' | 'webkit'
}) => {
  await page.goto('/');
});

Creating Custom Fixtures

Extend test with test.extend() to add your own fixtures:

TypeScript
// fixtures.ts
import { test as base, expect } from '@playwright/test';
import { LoginPage } from './pages/LoginPage';
import { DashboardPage } from './pages/DashboardPage';
import { ProductPage } from './pages/ProductPage';

// Define the fixture types
type MyFixtures = {
  loginPage: LoginPage;
  dashboardPage: DashboardPage;
  productPage: ProductPage;
  authenticatedPage: LoginPage;
};

// Extend the base test
export const test = base.extend<MyFixtures>({
  // Simple page object fixture
  loginPage: async ({ page }, use) => {
    const loginPage = new LoginPage(page);
    await use(loginPage); // 'use' provides the fixture to the test
    // Teardown happens after 'use' returns
  },

  dashboardPage: async ({ page }, use) => {
    await use(new DashboardPage(page));
  },

  productPage: async ({ page }, use) => {
    await use(new ProductPage(page));
  },

  // Fixture that logs in before the test
  authenticatedPage: async ({ page }, use) => {
    await page.goto('/login');
    await page.getByLabel('Email').fill('user@test.com');
    await page.getByLabel('Password').fill('pass123');
    await page.getByRole('button', { name: 'Sign in' }).click();
    await page.waitForURL('/dashboard');

    await use(new LoginPage(page));  // Test runs here

    // Optional: logout after test
    await page.getByRole('button', { name: 'Logout' }).click();
  },
});

export { expect } from '@playwright/test';

Using Custom Fixtures in Tests

TypeScript
// tests/dashboard.spec.ts
import { test, expect } from '../fixtures';

test('dashboard loads with auth fixture', async ({ dashboardPage, authenticatedPage }) => {
  // Already logged in — authenticatedPage fixture handled it
  await expect(dashboardPage.welcomeHeading).toBeVisible();
});

test('login page has correct title', async ({ loginPage }) => {
  await loginPage.navigate();
  await expect(loginPage.heading).toHaveText('Sign In');
});

Fixture Scope — Worker-Level Fixtures

By default, fixtures run per test. Use scope: 'worker' for shared setup:

TypeScript
type WorkerFixtures = {
  workerStorageState: string;
};

export const test = base.extend<{}, WorkerFixtures>({
  workerStorageState: [async ({ browser }, use) => {
    // Runs ONCE per worker — shared across tests in that worker
    const page = await browser.newPage();
    await page.goto('/login');
    await page.fill('#email', 'admin@test.com');
    await page.fill('#password', 'admin');
    await page.click('#submit');
    await page.context().storageState({ path: 'admin-auth.json' });
    await page.close();
    await use('admin-auth.json');
  }, { scope: 'worker' }],
});

Follow AutomateQA

Related Topics