Types of Fixtures in Playwright

Playwright Test supports built-in fixtures and custom fixtures. Fixtures can also be configured with different scopes and setup behaviors.

Let’s understand the most important concepts.

1. Built-in Fixtures

Playwright provides several fixtures that you can use directly in your test.

FixturePurpose
pageProvides a page for interacting with a website.
contextProvides an isolated browser context.
browserProvides the browser instance.
browserNameProvides the configured browser name.
requestProvides an API request context for making HTTP requests.

You do not need to manually create these fixtures in every test. Playwright Test provides them when you request them as test function parameters.

Playwright Fixtures such as page and context help you access browser resources without creating them manually in every test.

Example: Using the Page Fixture

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

test('Open login page', async ({ page }) => {
  await page.goto('https://example.com');

  await expect(page).toHaveURL('https://example.com/');
});

Explanation:

Notice that we did not manually create a browser page. Playwright provides it through the page fixture.

Note: This example assumes the website redirects to or uses the URL shown in the assertion. Adjust the expected URL for your application.

2. Custom Fixtures

Sometimes, built-in fixtures are not enough.

For example, suppose every test needs a common test user or a login workflow. You can create a custom fixture to provide that functionality.

Custom fixtures are useful when you want to share your own setup logic across test cases.

Let’s create a simple custom fixture that provides a welcome message.

First, create a file named fixtures.ts.

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

type MyFixtures = {
  welcomeMessage: string;
};

export const test = base.extend<MyFixtures>({
  welcomeMessage: async ({}, use) => {
    const message = 'Welcome to Playwright';

    await use(message);
  },
});

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

Now use the custom fixture in your test file:

import { test, expect } from './fixtures';

test('Verify welcome message', async ({ welcomeMessage }) => {
  expect(welcomeMessage).toBe('Welcome to Playwright');
});

How does it work?

This example uses a string to demonstrate the concept. You can also create fixtures that prepare pages, test data, authenticated sessions, or other resources.

What Is the use() Function in Playwright Fixtures?

The use() function is an important part of fixture implementation.

It passes the prepared resource to the test and allows Playwright to manage the fixture lifecycle.

Consider this example:

myFixture: async ({}, use) => {
  const data = 'Test Data';

  await use(data);

  // Cleanup code can go here.
}

The execution flow is:

  1. Playwright prepares the fixture.
  2. use(data) provides the value to the test.
  3. The test executes.
  4. After the test finishes, execution continues after use().
  5. Any cleanup code placed there can run.

For example, if a custom fixture creates a temporary resource, you can release that resource after the test finishes.

Remember: The code before use() is the setup phase, and the code after use() is the teardown phase.

Test Fixtures vs Worker Fixtures

Playwright supports different fixture scopes. Two important scopes are test and worker.

Test-scoped fixtures

A test-scoped fixture is created when needed for a test and is cleaned up after that test finishes.

This is the default scope for custom fixtures.

Example:

testData: async ({}, use) => {
  const data = { username: 'testuser' };

  await use(data);
}

This is useful when each test needs its own data or resource.

Worker-scoped fixtures

A worker-scoped fixture can be shared by tests running in the same worker process.

It is useful for resources that are expensive to initialize and can safely be shared, such as certain connections or worker-level setup.

Example:

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

type WorkerFixtures = {
  environmentName: string;
};

export const test = base.extend<{}, WorkerFixtures>({
  environmentName: [
    async ({}, use) => {
      await use('QA Environment');
    },
    { scope: 'worker' },
  ],
});

Here, environmentName is configured with worker scope.

Important difference: Test-scoped fixtures are associated with individual tests, while worker-scoped fixtures are shared within a worker. Worker fixtures should not hold mutable state that individual tests could accidentally interfere with.

Pages: 1 2 3 4 5 6

Leave a Reply

Your email address will not be published. Required fields are marked *