Implementation:Microsoft Playwright Test Function
| Knowledge Sources | |
|---|---|
| Domains | Testing, Browser_Automation |
| Last Updated | 2026-02-11 00:00 GMT |
Overview
Concrete tool for structuring test code into executable test cases with grouping, hooks, and fixtures, provided by the Playwright library.
Description
The test function is the core API for defining test cases in Playwright Test. It is exported from the @playwright/test package and serves as both a function (for defining individual tests) and a namespace (for accessing describe, beforeEach, afterEach, beforeAll, afterAll, and other organizational methods).
Internally, the test function is implemented in testType.ts as a TestTypeImpl class. When test(title, body) is called, it creates a TestCase object and registers it in the current Suite. The test.describe() method creates nested Suite instances. Lifecycle hooks like test.beforeEach() push hook functions onto the current suite's hook arrays. All registration uses wrapFunctionWithLocation to capture source locations for error reporting.
The test body function receives fixtures as its argument. Playwright's built-in fixtures include page, context, browser, and request. Custom fixtures can be defined using test.extend(), which creates a new test type with additional fixtures available.
Usage
Use the test function in every Playwright test file to define individual test cases. Use test.describe to group related tests. Use test.beforeEach and test.afterEach for shared setup and teardown. Use test.extend when custom fixtures are needed across multiple test files.
Code Reference
Source Location
- Repository: playwright
- File:
packages/playwright/src/common/testType.ts(lines 39-184)
Signature
// Define a test case
function test(title: string, body: (args: PlaywrightTestArgs & PlaywrightTestOptions, testInfo: TestInfo) => Promise<void> | void): void;
// Define a test case with additional details
function test(title: string, details: TestDetails, body: (args: PlaywrightTestArgs & PlaywrightTestOptions, testInfo: TestInfo) => Promise<void> | void): void;
// Group tests into a describe block
test.describe(title: string, callback: () => void): void;
// Define a beforeEach hook
test.beforeEach(fn: (args: PlaywrightTestArgs & PlaywrightTestOptions, testInfo: TestInfo) => Promise<void> | void): void;
// Define an afterEach hook
test.afterEach(fn: (args: PlaywrightTestArgs & PlaywrightTestOptions, testInfo: TestInfo) => Promise<void> | void): void;
// Define a beforeAll hook
test.beforeAll(fn: (args: PlaywrightWorkerArgs & PlaywrightWorkerOptions, workerInfo: WorkerInfo) => Promise<void> | void): void;
// Define an afterAll hook
test.afterAll(fn: (args: PlaywrightWorkerArgs & PlaywrightWorkerOptions, workerInfo: WorkerInfo) => Promise<void> | void): void;
// Extend test with custom fixtures
test.extend<T>(fixtures: Fixtures<T>): TestType<PlaywrightTestArgs & T, PlaywrightWorkerArgs>;
Import
import { test } from '@playwright/test';
I/O Contract
Inputs
| Name | Type | Required | Description |
|---|---|---|---|
| title | string |
Yes | The name of the test case or describe block. Used for identification, filtering, and reporting. |
| details | TestDetails |
No | Optional object with tag and annotation fields for categorizing and annotating tests.
|
| body | Function |
Yes | The test body function that receives destructured fixtures (e.g., { page }) and a TestInfo object. For test.describe, this is a synchronous callback that contains nested test() calls.
|
| fn (hooks) | Function |
Yes | Hook function for beforeEach, afterEach, beforeAll, afterAll. Receives the same fixture arguments as test bodies.
|
| fnOrDetails | TestDetails | Yes | For the overloaded form, either the test body directly or a TestDetails object followed by the body. |
Outputs
| Name | Type | Description |
|---|---|---|
| TestCase | TestCase (internal) |
Calling test() registers a TestCase instance in the current Suite. This is an internal data structure used by the test runner.
|
| Suite | Suite (internal) |
Calling test.describe() creates a nested Suite within the current suite hierarchy.
|
| Hook registration | void |
Calling test.beforeEach() or similar methods pushes the hook function onto the current suite's hook array.
|
Usage Examples
Basic Example
import { test, expect } from '@playwright/test';
test('has title', async ({ page }) => {
await page.goto('https://playwright.dev/');
await expect(page).toHaveTitle(/Playwright/);
});
test('get started link', async ({ page }) => {
await page.goto('https://playwright.dev/');
await page.getByRole('link', { name: 'Get started' }).click();
await expect(page).toHaveURL(/.*intro/);
});
Describe and Hooks Example
import { test, expect } from '@playwright/test';
test.describe('navigation', () => {
test.beforeEach(async ({ page }) => {
await page.goto('https://playwright.dev/');
});
test('main navigation', async ({ page }) => {
await expect(page.getByRole('link', { name: 'Docs' })).toBeVisible();
});
test('search functionality', async ({ page }) => {
await page.getByRole('button', { name: 'Search' }).click();
await expect(page.getByRole('dialog')).toBeVisible();
});
});
Custom Fixtures Example
import { test as base, expect } from '@playwright/test';
type MyFixtures = {
todoPage: Page;
};
const test = base.extend<MyFixtures>({
todoPage: async ({ page }, use) => {
await page.goto('https://demo.playwright.dev/todomvc');
await use(page);
},
});
test('should add a todo item', async ({ todoPage }) => {
await todoPage.getByPlaceholder('What needs to be done?').fill('Buy milk');
await todoPage.getByPlaceholder('What needs to be done?').press('Enter');
await expect(todoPage.getByTestId('todo-title')).toHaveText('Buy milk');
});