🎭 Why Do I Choose Playwright for End-to-End Testing?

Some of the features of Playwright I like over other e2e testing frameworks.


Introduction

Playwright enables reliable end-to-end testing for modern web apps. Playwright Test was created specifically to accommodate the needs of end-to-end testing. Playwright supports all modern rendering engines including Chromium, WebKit, and Firefox. Test on Windows, Linux, and macOS, locally or on CI, headless or headed, with native mobile emulation for Google Chrome for Android and Mobile Safari.

Key advantages:

  • Cross-browser support: Test on Chromium, Firefox, WebKit, Chrome, Edge, and mobile devices.
  • First-class TypeScript support: Strong typing and autocompletion.
  • Parallel execution: Fast test runs with isolated browser contexts.
  • Powerful automation APIs: Interact with pages, network, and browser features.
  • Rich debugging tools: UI mode, trace viewer, and codegen.

See Best Practices for more tips.


Out-of-the-box Test Assertions

Playwright includes test assertions via the expect function. To make an assertion, call expect(value) and choose a matcher that reflects your expectation. There are many generic matchers like toEqual, toContain, and toBeTruthy for asserting various conditions.

Soft Assertions

By default, a failed assertion will terminate test execution. Playwright also supports soft assertions: failed soft assertions do not terminate test execution, but mark the test as failed.

// Make a few checks that will not stop the test when failed...
await expect.soft(page.getByTestId('status')).toHaveText('Success')
await expect.soft(page.getByTestId('eta')).toHaveText('1 day')
// ... and continue the test to check more things.
await page.getByRole('link', { name: 'next page' }).click()
await expect
  .soft(page.getByRole('heading', { name: 'Make another order' }))
  .toBeVisible()

At any point during test execution, you can check for soft assertion failures:

// Make a few checks that will not stop the test when failed...
await expect.soft(page.getByTestId('status')).toHaveText('Success')
await expect.soft(page.getByTestId('eta')).toHaveText('1 day')
// Avoid running further if there were soft assertion failures.
expect(test.info().errors).toHaveLength(0)

Note: Soft assertions only work with the Playwright test runner.


Support for Multiple Browsers and Devices

Playwright can run tests on Chromium, WebKit, and Firefox, as well as branded browsers like Google Chrome and Microsoft Edge. It can also emulate tablets and mobile devices, including device orientation, geolocation, and color scheme.

You can run your tests in multiple browsers and configurations by setting up projects in the config. You can also add different options for each project.

import { defineConfig, devices } from '@playwright/test'
 
export default defineConfig({
  projects: [
    // Desktop browsers
    { name: 'chromium', use: { ...devices['Desktop Chrome'] } },
    { name: 'firefox', use: { ...devices['Desktop Firefox'] } },
    { name: 'webkit', use: { ...devices['Desktop Safari'] } },
    // Mobile emulation
    { name: 'Mobile Chrome', use: { ...devices['Pixel 5'] } },
    { name: 'Mobile Safari', use: { ...devices['iPhone 12'] } },
    // Branded browsers
    {
      name: 'Google Chrome',
      use: { ...devices['Desktop Chrome'], channel: 'chrome' },
    },
    {
      name: 'Microsoft Edge',
      use: { ...devices['Desktop Edge'], channel: 'msedge' },
    },
  ],
})

Certain Enterprise Browser Policies may impact Playwright's ability to launch and control Google Chrome and Microsoft Edge. Running in an environment with browser policies is outside of the Playwright project's scope.


Generating Tests

Use the codegen command to run the test generator, followed by the URL of the website you want to generate tests for. The URL is optional; you can always run the command without it and add the URL directly in the browser window.

npx playwright codegen demo.playwright.dev/todomvc

Interact with the browser, and Playwright will generate code for your actions. Codegen prioritizes role, text, and test id locators, and will refine locators to uniquely identify elements, reducing test flakiness.

You can also generate tests using emulation for specific viewports, devices, or color schemes, as well as emulate geolocation, language, or timezone. The generator can also preserve authenticated state. See the Test Generator guide for more.

With the test generator, you can record:

  • assert visibility to check if an element is visible
  • assert text to check if an element contains specific text
  • assert value to check if an element has a specific value

Debug Tests in UI Mode

Debug your tests with UI Mode for a better developer experience. You can walk through each step and visually see what was happening before, during, and after each step. UI mode also includes features like the locator picker and watch mode.

npx playwright test --ui

Use the Pick Locator button to select an element and see the locator Playwright would use. Edit locators in the locator playground and see them highlighted live. Use Copy Locator to copy and paste into your test.

To debug one test file:

npx playwright test example.spec.ts --debug

To debug a specific test from a line number:

npx playwright test example.spec.ts:10 --debug

Test Reports

The HTML Reporter shows a full report of your tests, allowing you to filter by browsers, passed, failed, skipped, and flaky tests. By default, the HTML report opens automatically if some tests fail, or you can open it manually:

npx playwright show-report

You can also use other reporters (list, dot, JSON, JUnit, Allure, etc.) for integration with CI/CD pipelines.


Trace Viewer

Playwright Trace Viewer is a GUI tool to explore recorded Playwright traces of your tests. You can go back and forward through each action and visually see what was happening.

In the HTML report, click the trace icon next to the test name to open the trace for that test. Playwright HTML Report Or, in the detailed view, scroll to the Traces tab and open the trace by clicking the screenshot. Playwright HTML Report Detailed View

View traces by clicking through each action or hovering on the timeline. Inspect the log, source, network, errors, and console during each step. The trace viewer creates a DOM snapshot so you can interact with it and open DevTools to inspect HTML, CSS, etc. Playwright Trace Viewer See the Trace Viewer guide for more.


Support for Multiple Languages

Playwright is available in multiple languages (JavaScript/TypeScript, Python, Java, .NET, Go) that share the same underlying implementation. All core browser automation features are supported in all languages, though testing ecosystem integration may differ. See supported languages.


Mock APIs and Network Interception

Playwright provides APIs to mock and modify network traffic, both HTTP and HTTPS. You can also mock using HAR files. For example, intercept all calls to */**/api/v1/fruits and return a custom response:

test("mocks a fruit and doesn't call api", async ({ page }) => {
  // Mock the api call before navigating
  await page.route('*/**/api/v1/fruits', async (route) => {
    const json = [{ name: 'Strawberry', id: 21 }]
    await route.fulfill({ json })
  })
  // Go to the page
  await page.goto('https://demo.playwright.dev/api-mocking')
  // Assert that the Strawberry fruit is visible
  await expect(page.getByText('Strawberry')).toBeVisible()
})

For microservices or large websites, you can proxy partial JS and CSS file requests to your local dev server, making it easier to develop test cases when you need to change source code or add test ids.

export default defineConfig({
  // ...
  webServer: {
    command: 'npm run start',
    url: 'http://127.0.0.1:3000',
    reuseExistingServer: !process.env.CI,
  },
})

See webServer for more.


Out-of-the-box Snapshot Testing

Playwright provides methods for comparing page and element screenshots with expected values stored in files.

import { test, expect } from '@playwright/test'
 
test('example test', async ({ page }) => {
  await page.goto('https://playwright.dev')
  // Basic usage.
  await expect(page).toHaveScreenshot()
})
// Pass options to customize the snapshot comparison and have a generated name.
await expect(page).toHaveScreenshot('landing-page.png', { maxDiffPixels: 100 })
// Configure image matching threshold.
expect(await page.screenshot()).toMatchSnapshot('landing-page.png', {
  threshold: 0.3,
})
// Non-image snapshots.
expect(await page.textContent('.hero__title')).toMatchSnapshot('hero.txt')

Learn more about visual comparisons.

Note: Matching snapshots only works with the Playwright test runner.

Since there can be significant differences among snapshots on different OS or headed/headless mode, it's better to run snapshot test cases on Linux headless mode using the Docker image to keep the same environment as CI.


Integration with Docker

Script to run Docker image:

docker run -it --rm --ipc=host -v $(pwd):/work/ -w /work/ mcr.microsoft.com/playwright:v1.42.1-jammy /bin/bash

See usage for more.


Why Playwright Over Other Frameworks?

  • True cross-browser and cross-platform support (including WebKit/Safari).
  • Powerful and reliable locators (role, text, test id, etc.) reduce test flakiness.
  • Rich debugging and reporting tools (UI mode, trace viewer, HTML reports).
  • First-class support for modern web features (SPA, SSR, network mocking, mobile emulation).
  • Fast parallel execution and isolated browser contexts.
  • Active development and strong community support.

Further Reading