Playwright Testing

This chapter guides you through writing and running your first Playwright test, helping you understand the basic components and execution flow of a test.


Run the example test

After initializing the project, first run the automatically generated example test to make sure everything is working:

# 运行所有测试
npx playwright test

After running it, you will see output similar to the following:

Running 6 tests using 4 workers

  ✓  1 [chromium] › tests/example.spec.ts:3:1 › has title (2.1s)
  ✓  2 [chromium] › tests/example.spec.ts:10:1 › get started link (1.8s)
  ✓  3 [firefox] › tests/example.spec.ts:3:1 › has title (1.9s)
  ✓  4 [firefox] › tests/example.spec.ts:10:1 › get started link (1.7s)
  ✓  5 [webkit] › tests/example.spec.ts:3:1 › has title (2.3s)
  ✓  6 [webkit] › tests/example.spec.ts:10:1 › get started link (2.0s)

  6 passed (12s)

From the output, you can see: the tests ran 2 tests on each of 3 browsers, for a total of 6 tests, all passed.


Create your first test file

Cleartests/example.spec.tsthe contents, and let's write a test from scratch.

Example

// File path: tests/first-test.spec.ts
import { test, expect } from '@playwright/test';

// test(name, callback) defines a test
// name: the test name, displayed in the test report
// The callback parameter { page } is a fixture provided by Playwright
test('Visit the EXAMPLE homepage and check the title', async ({ page }) => {
  // Navigate to the specified URL
  await page.goto('https://www.example.com/');

  // Assertion: the page title contains "EXAMPLE"
  await expect(page).toHaveTitle(/EXAMPLE/);
});

Run this test:

npx playwright test tests/first-test.spec.ts

Expected output:

Running 1 test using 1 worker

  ✓  1 [chromium] › tests/first-test.spec.ts:4:1 › 访问 EXAMPLE 首页并检查标题 (2.5s)

  1 passed (2.5s)

Detailed explanation of the test() function

test()It is the core function of Playwright Test, used to define a test case.

Basic syntax

Example

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

// test(test name, test function)
test('Descriptive name of the test', async ({ page }) => {
  // Operations in the test
  await page.goto('https://example.com');

  // Assertions in the test
  await expect(page).toHaveTitle(/Example/);
});

Parameter description

ParameterTypeDescription
First parameter (test name)stringThe descriptive name of the test, displayed in test reports and logs.
Second parameter (test function)async (fixtures) => {}An asynchronous function containing the actual test logic.

What is the page fixture?

{ page }It is the one in the test function parametersdestructuring assignmentit extracts from the fixture object provided by Playwrightpage。

pageis aPage objectrepresenting a browser tab.

Each test has its own independentpageinstance:

fixtureTypeDescription
pagePage objectIndependent browser tab, used for navigation, actions, and assertions
contextBrowserContext objectBrowser context, manages cookies, storage, etc.
browserBrowser objectBrowser instance, rarely used directly
requestAPIRequestContext objectUsed to send HTTP requests (not through the browser)

The most commonly used ispage, the vast majority of operations are done through it.


page.goto() navigation

page.goto(url)It is one of the most basic operations in Playwright, used to navigate to a specified URL.

Example

// Basic usage
await page.goto('https://www.example.com/');

// Navigation with options
await page.goto('https://www.example.com/', {
  // Wait until the 'load' event fires (default value)
  waitUntil: 'load',
  // Operation timeout (milliseconds)
  timeout: 30000,
  // Referrer (Referer header)
  referer: 'https://www.google.com/',
});

waitUntilThe option has three possible values:

ValueMeaningApplicable scenario
'load'Wait for the load event to fireDefault value, suitable for most pages
'domcontentloaded'Wait for the DOMContentLoaded eventYou only need the page's basic structure to be loaded
'networkidle'Wait for the network to be idle (no new requests within 500ms)When you need to wait for all asynchronous data to be loaded

In the vast majority of cases, use the default'load'and that's it. Playwright's automatic waiting mechanism will handle subsequent element availability issues, so there is no need to use'networkidle'。


expect() assertions

expect()It is Playwright's assertion method, used to verify whether test results meet expectations.

Example

// Page title assertion
await expect(page).toHaveTitle(/EXAMPLE/);

// Page URL assertion
await expect(page).toHaveURL('https://www.example.com/');

// Element visibility assertion
await expect(page.getByText('EXAMPLE')).toBeVisible();

Playwright's assertions have a key feature:automatic retry。

If the assertion condition is not met temporarily, Playwright will keep retrying until the condition is met or a timeout occurs.

This means you don't need to writewaitFororsleepto wait for the page to be ready.


The async/await pattern in tests

All Playwright operations are asynchronous, so test functions must be declared asasync。

All browser operations (goto、click、filletc.) and assertions (expect) must useawaitkeyword(s).

Example

// Correct way
test('Correct example', async ({ page }) => {
  await page.goto('https://www.example.com/');
  await expect(page).toHaveTitle(/EXAMPLE/);
});

// Incorrect way (missing await)
test('Incorrect example', async ({ page }) => {
  page.goto('https://www.example.com/');        // No await!
  expect(page).toHaveTitle(/EXAMPLE/);           // No await!
});

If you forget to writeawait, the test may assert at the wrong time, causing instability or accidental passes. Be sure to add it every time you call the Playwright APIawait。


Understanding test output

Let's look at another test with a failing assertion to understand test output:

Example

// File path: tests/fail-demo.spec.ts
import { test, expect } from '@playwright/test';

test('A deliberately failing test', async ({ page }) => {
  await page.goto('https://www.example.com/');

  // Assert that the title contains non-existent content; this is expected to fail
  await expect(page).toHaveTitle(/non-existent text/);
});

After running, the output looks roughly like this:

Running 1 test using 1 worker

  ✘  1 [chromium] › tests/fail-demo.spec.ts:4:1 › 一个故意失败的测试 (5.2s)

  1 failed
    tests/fail-demo.spec.ts:4:1 › 一个故意失败的测试

At the same time, an HTML report will automatically open, showing detailed failure information and error screenshots.

Other extensions