Playwright Masters

Playwright with Python: Complete pytest-playwright Guide

Playwright with Python lets testers automate modern web applications using Python while controlling Chromium, Firefox, and WebKit browsers.

When Playwright is combined with pytest, testers can organize browser tests, reuse setup through fixtures, run the same tests across browsers, group tests with markers, and generate repeatable automated test runs.

To use Playwright with Python, install pytest-playwright, install the required browser binaries, create a pytest test that receives the page fixture, and run it with pytest. The Playwright pytest plugin manages the browser setup so you can focus on test actions and assertions.

Table of Contents

A minimal Playwright Python test looks like this:

from playwright.sync_api import Page, expect

 

def test_homepage(page: Page):

    page.goto(“https://playwright.dev/”)

    expect(page).to_have_title(“Fast and reliable end-to-end testing for modern web apps | Playwright”)

This guide goes beyond a first script. You will learn how pytest and Playwright work together, how to use fixtures correctly, how to test multiple browsers, how to organize Python tests, and how to debug failures.

Playwright Python

What Is Playwright with Python?

Playwright with Python is the Python language binding for Playwright browser automation.

It allows Python programs to open browsers, navigate web pages, find elements, perform user actions, inspect page content, and verify application behavior.

For automated testing, Playwright can be combined with pytest.

This creates two clear responsibilities:

  • Playwright controls and observes the browser.
  • pytest discovers, organizes, runs, and reports tests.
  • pytest-playwright connects Playwright with pytest through useful fixtures and command-line options.

Understanding this separation is important because Playwright itself and pytest solve different parts of the testing problem.

What Is pytest-playwright?

pytest-playwright is the official pytest plugin for Playwright.

It provides fixtures such as page, context, and browser that tests can request directly.

Without the plugin, you can still use the Playwright Python library, but you would need to manage more browser setup and cleanup yourself.

With the plugin, a test can simply request a page:

from playwright.sync_api import Page

 

def test_example(page: Page):

    page.goto(“https://example.com”)

pytest sees the page argument and gets the fixture supplied by the plugin.

This keeps basic tests small and readable.

Playwright Python vs Playwright TypeScript

Playwright supports several programming languages, but the surrounding test experience is not identical in every language.

The TypeScript/JavaScript ecosystem commonly uses Playwright Test.

Python projects commonly combine Playwright with pytest.

Area

Python approach

TypeScript approach

Language

Python

TypeScript/JavaScript

Common test runner

pytest

Playwright Test

Test reuse

pytest fixtures

Playwright fixtures

Assertions

Playwright expect + pytest assertions

Playwright expect

Test markers/tags

pytest markers

Playwright tags/annotations

Package management

pip

npm

Typical test file

test_login.py

login.spec.ts

Choose Python when your team already works comfortably with Python or when your broader automation ecosystem uses Python.

Choose TypeScript when your team prefers the first-party Playwright Test runner and the JavaScript/TypeScript ecosystem.

The better choice depends on the project and team, not on a universal rule that one language is always superior.

How to Install Playwright for Python

For a pytest-based project, create a Python environment and install the pytest Playwright plugin.

A common command is:

pip install pytest-playwright

Then install Playwright’s browser binaries:

playwright install

The browser installation is a separate step because the Python package and browser executables are different components.

After installation, verify that pytest can run:

pytest –version

For a detailed environment setup walkthrough, use the Playwright Masters Playwright installation guide rather than duplicating the full general installation process here.

Recommended Playwright Python Project Structure

A beginner project can start small:

playwright-python/

│

├── tests/

│   ├── test_home.py

│   └── test_login.py

│

├── conftest.py

├── pytest.ini

└── requirements.txt

As the project grows, separate reusable responsibilities:

playwright-python/

│

├── tests/

│   ├── test_login.py

│   ├── test_search.py

│   └── test_checkout.py

│

├── pages/

│   ├── login_page.py

│   └── checkout_page.py

│

├── utils/

│   └── helpers.py

│

├── conftest.py

├── pytest.ini

└── requirements.txt

Do not create dozens of folders before the project needs them.

A good test structure makes code easier to find. A complicated folder structure does not automatically make a framework better.

Your First Playwright Python Test

Create:

tests/test_home.py

Add:

from playwright.sync_api import Page, expect

 

def test_playwright_homepage(page: Page):

    page.goto(“https://playwright.dev/”)

 

    expect(page).to_have_title(

        “Fast and reliable end-to-end testing for modern web apps | Playwright”

    )

Run it:

pytest

pytest discovers functions whose names follow its test discovery conventions.

The page object represents a browser tab.

page.goto() navigates to a URL.

expect() performs an assertion.

The test succeeds only when the expected condition is satisfied within the configured behavior and timeout.

How the page Fixture Works

The page fixture is one of the most useful parts of pytest-playwright.

Instead of manually writing browser startup and shutdown logic in every test, request the fixture:

def test_page(page):

    page.goto(“https://example.com”)

The plugin handles the browser resources required by that fixture.

This gives each test a cleaner starting point.

For type hints, use:

from playwright.sync_api import Page

 

def test_page(page: Page):

    page.goto(“https://example.com”)

Type hints are not required for Playwright to run, but they improve editor support and make code easier to understand.

Important pytest-playwright Fixtures

Different fixtures operate at different browser levels.

page

Use page for most ordinary browser tests.

def test_title(page):

    page.goto(“https://example.com”)

    assert “Example” in page.title()

context

A browser context is an isolated browser session.

It can have its own cookies, storage, permissions, and pages.

def test_new_page(context):

    page = context.new_page()

    page.goto(“https://example.com”)

 

    assert “Example” in page.title()

Browser contexts are useful when a test needs more control over the browser session.

browser

The browser fixture represents the browser instance.

You can use it to create contexts manually:

def test_custom_context(browser):

    context = browser.new_context(

        viewport={“width”: 1280, “height”: 720}

    )

 

    page = context.new_page()

    page.goto(“https://example.com”)

 

    context.close()

Use the highest-level fixture that solves the problem.

If page is enough, manually managing browsers and contexts only adds unnecessary code.

Playwright Python Locators

A locator describes how Playwright should find an element.

Good locators represent how users understand the interface whenever possible.

Consider this HTML:

<button>Sign in</button>

A user understands this as a button named “Sign in.”

The corresponding locator can be:

page.get_by_role(“button”, name=”Sign in”)

Click it with:

page.get_by_role(“button”, name=”Sign in”).click()

Recommended Locator Types

Useful user-facing locators include:

page.get_by_role(“button”, name=”Submit”)

page.get_by_label(“Email”)

page.get_by_placeholder(“Search”)

page.get_by_text(“Welcome”)

page.get_by_test_id(“checkout-button”)

The best locator depends on the element and the application.

Avoid choosing a locator only because it is short.

Choose one that remains meaningful when harmless HTML implementation details change.

For a complete locator strategy, link this section to the dedicated Playwright Locators article rather than repeating the entire locator topic here.

That keeps this Python tutorial focused on Python.

Fill a Form with Playwright Python

Suppose a login form contains an email field, password field, and sign-in button.

A Python test can look like this:

from playwright.sync_api import Page, expect

 

def test_login_form(page: Page):

    page.goto(“https://example.com/login”)

 

    page.get_by_label(“Email”).fill(“student@example.com”)

    page.get_by_label(“Password”).fill(“example-password”)

    page.get_by_role(“button”, name=”Sign in”).click()

 

    expect(page.get_by_text(“Dashboard”)).to_be_visible()

This demonstrates a useful test pattern:

Arrange → Act → Verify

First, open the required application state.

Next, perform the user action.

Finally, verify the result.

Avoid putting real production passwords directly in source code. Use test accounts and secure environment configuration where credentials are required.

Playwright Python Assertions

An automated test should verify something meaningful.

Playwright’s expect API provides web-aware assertions.

Example:

expect(page.get_by_role(“heading”)).to_contain_text(“Welcome”)

Visibility:

expect(page.get_by_role(“button”, name=”Checkout”)).to_be_visible()

URL:

expect(page).to_have_url(“https://example.com/dashboard”)

Element count:

expect(page.locator(“.product”)).to_have_count(5)

Input value:

expect(page.get_by_label(“Email”)).to_have_value(

    “student@example.com”

)

Web-aware assertions are valuable because modern pages often update asynchronously.

Do not replace them with arbitrary sleeps.

Why You Usually Do Not Need time.sleep()

A common beginner mistake is:

import time

 

page.get_by_role(“button”, name=”Submit”).click()

time.sleep(5)

The test waits five seconds whether the application needs 100 milliseconds or the full five seconds.

That makes tests slower and may still fail if the application needs longer.

Playwright actions have built-in actionability checks, and Playwright assertions can retry while waiting for their expected condition.

A better approach is:

page.get_by_role(“button”, name=”Submit”).click()

 

expect(

    page.get_by_text(“Order confirmed”)

).to_be_visible()

Now the test waits for a meaningful application condition instead of waiting for an arbitrary amount of time.

Run Playwright Python Tests in Headed Mode

Tests commonly run without a visible browser.

During debugging, you may want to see the browser.

Run:

pytest –headed

This opens the browser UI during execution.

Headed mode is useful for understanding unexpected test behavior.

It is a debugging option, not a replacement for assertions.

Run Tests in Chromium, Firefox and WebKit

Cross-browser execution is one of Playwright’s important capabilities.

Run Chromium:

pytest –browser chromium

Run Firefox:

pytest –browser firefox

Run WebKit:

pytest –browser webkit

You can also request multiple browsers:

pytest \

  –browser chromium \

  –browser firefox \

  –browser webkit

The exact shell formatting can differ by operating system.

Cross-browser testing matters because an application that works in one browser engine may behave differently in another.

However, running every test against every browser may increase execution time.

Teams often select browser coverage according to user traffic, risk, and product requirements.

Use pytest Fixtures for Reusable Setup

pytest fixtures let you create reusable test setup.

Suppose many tests must start on the login page.

In conftest.py:

import pytest

from playwright.sync_api import Page

 

@pytest.fixture

def login_page(page: Page):

    page.goto(“https://example.com/login”)

    return page

Then:

def test_login_page_title(login_page):

    assert “Login” in login_page.title()

Fixtures reduce repeated setup code.

But do not turn every two-line action into a fixture.

Fixtures are most useful when they represent reusable test dependencies or setup.

Create a Logged-In pytest Fixture

Authentication is a common reusable condition.

For a simple demonstration:

import pytest

from playwright.sync_api import Page

 

@pytest.fixture

def logged_in_page(page: Page):

    page.goto(“https://example.com/login”)

 

    page.get_by_label(“Email”).fill(“test@example.com”)

    page.get_by_label(“Password”).fill(“example-password”)

    page.get_by_role(“button”, name=”Sign in”).click()

 

    page.wait_for_url(“**/dashboard”)

 

    return page

A test can use it:

from playwright.sync_api import expect

 

def test_dashboard(logged_in_page):

    expect(

        logged_in_page.get_by_role(

            “heading”,

            name=”Dashboard”

        )

    ).to_be_visible()

For large suites, repeatedly logging in through the UI can be inefficient.

A mature project may create authenticated state once and reuse appropriate storage state.

That deserves its own authentication guide rather than expanding this Python tutorial into another topic.

Parameterize Playwright Tests with pytest

pytest can run one test with several input values.

Example:

import pytest

from playwright.sync_api import Page, expect

 

@pytest.mark.parametrize(

    “search_term”,

    [

        “Playwright”,

        “Python”,

        “pytest”,

    ],

)

def test_search(page: Page, search_term):

    page.goto(“https://example.com/search”)

 

    page.get_by_placeholder(“Search”).fill(search_term)

    page.get_by_role(“button”, name=”Search”).click()

 

    expect(page.get_by_text(search_term).first).to_be_visible()

pytest creates a separate test case for each parameter.

This is useful for small sets of inline values.

For large external datasets stored in JSON, CSV, or Excel, use the dedicated Playwright Data-Driven Testing guide.

This separation prevents the Python tutorial and data-driven article from competing for the same search intent.

Use pytest Markers to Organize Tests

Markers let you label groups of tests.

Example:

import pytest

 

@pytest.mark.smoke

def test_homepage(page):

    page.goto(“https://example.com”)

Another test:

@pytest.mark.regression

def test_checkout(page):

    page.goto(“https://example.com/checkout”)

Run only smoke tests:

pytest -m smoke

Markers are useful when CI pipelines need different test groups.

Register custom markers in project configuration to avoid warnings and keep their purpose documented.

For example, pytest.ini:

[pytest]

markers =

    smoke: critical checks for major user journeys

    regression: broader regression coverage

Clear marker names make the test suite easier to operate.

Run a Specific Playwright Python Test

Run one file:

pytest tests/test_login.py

Run one function:

pytest tests/test_login.py::test_valid_login

Run tests matching a name:

pytest -k login

These commands are useful during development because you do not need to execute the entire suite after every small change.

Configure a Base URL

Repeating the full application URL in every test creates unnecessary duplication.

pytest-playwright supports a base URL workflow when the relevant plugin configuration is available.

For example, run:

pytest –base-url https://example.com

Then a test can navigate using a relative path:

def test_login(page):

    page.goto(“/login”)

This makes it easier to run the same tests against different environments.

For example:

  • local development
  • QA
  • staging

Environment URLs should be configuration, not scattered throughout every test file.

Take Screenshots in Playwright Python

Screenshots are useful when investigating visual state.

Capture a page:

page.screenshot(path=”screenshots/homepage.png”)

Capture the full page:

page.screenshot(

    path=”screenshots/full-page.png”,

    full_page=True

)

Capture a specific element:

page.get_by_role(

    “main”

).screenshot(path=”screenshots/main.png”)

Screenshots show what the browser displayed at a moment in time.

They do not explain every action that happened before a failure.

Use them as debugging evidence rather than treating them as the only debugging tool.

Use Playwright Trace Viewer with Python Tests

Trace Viewer helps investigate test execution.

With pytest-playwright, tracing can be enabled through supported command-line options.

For example:

pytest –tracing retain-on-failure

When a trace is retained, it can help you inspect actions and browser state related to a failed test.

Tracing is especially valuable for failures that occur in CI but are difficult to reproduce locally.

Do not retain unnecessary traces forever because they consume storage.

Sync API vs Async API in Playwright Python

Playwright for Python provides synchronous and asynchronous APIs.

A synchronous test is easy for many beginners to read:

from playwright.sync_api import Page

 

def test_sync(page: Page):

    page.goto(“https://example.com”)

An async Playwright script uses async and await:

import asyncio

from playwright.async_api import async_playwright

 

async def main():

    async with async_playwright() as p:

        browser = await p.chromium.launch()

 

        page = await browser.new_page()

        await page.goto(“https://example.com”)

 

        await browser.close()

 

asyncio.run(main())

Do not choose async simply because it appears more advanced.

Use the programming model that fits your project and libraries.

For testers learning pytest-playwright for ordinary browser automation, the synchronous fixture-based approach provides a clear starting point.

Page Object Model with Playwright Python

As tests grow, repeated page interactions can be moved into reusable classes.

Example:

from playwright.sync_api import Page

 

class LoginPage:

    def __init__(self, page: Page):

        self.page = page

        self.email = page.get_by_label(“Email”)

        self.password = page.get_by_label(“Password”)

        self.sign_in = page.get_by_role(

            “button”,

            name=”Sign in”

        )

 

    def open(self):

        self.page.goto(“https://example.com/login”)

 

    def login(self, email: str, password: str):

        self.email.fill(email)

        self.password.fill(password)

        self.sign_in.click()

Use it in a test:

from playwright.sync_api import Page, expect

from pages.login_page import LoginPage

 

def test_valid_login(page: Page):

    login = LoginPage(page)

 

    login.open()

    login.login(

        “test@example.com”,

        “example-password”

    )

 

    expect(page).to_have_url(

        “https://example.com/dashboard”

    )

A page object should improve readability.

Do not hide every assertion and every test decision inside page objects.

The test should still make its purpose clear.

For full framework architecture, fixtures, folder design, and reusable components, link to the Playwright Framework pillar instead of duplicating it here.

Run Playwright Python Tests in Parallel

pytest itself does not automatically mean every test runs in parallel.

A common pytest solution is pytest-xdist.

Install it:

pip install pytest-xdist

Run with multiple workers:

pytest -n auto

Parallel execution can reduce overall test time when tests are properly isolated.

However, parallelism can expose bad test design.

Tests may conflict when they share:

  • the same user account,
  • the same database record,
  • the same downloaded filename,
  • mutable global state,
  • or another shared resource.

A test suite should be designed for isolation before adding aggressive parallel execution.

Run Playwright Python in CI

A Playwright Python suite can run inside a CI pipeline.

A basic GitHub Actions example is:

name: Playwright Python Tests

 

on:

  push:

  pull_request:

 

jobs:

  test:

    runs-on: ubuntu-latest

 

    steps:

      – uses: actions/checkout@v4

 

      – uses: actions/setup-python@v5

        with:

          python-version: “3.12”

 

      – name: Install Python dependencies

        run: |

          python -m pip install –upgrade pip

          pip install pytest-playwright

          playwright install –with-deps

 

      – name: Run tests

        run: pytest

A real project should normally install pinned project dependencies from its dependency file instead of installing one package directly inside CI.

For example:

pip install -r requirements.txt

This helps local and CI environments use the same declared dependencies.

Common Playwright Python Mistakes

Mistake 1: Using time.sleep() Everywhere

Hard waits make tests slower and less adaptive.

Wait for meaningful application conditions instead.

Mistake 2: Using Fragile CSS Selectors for Everything

A selector such as:

page.locator(

    “div:nth-child(4) > div > button”

)

may break after harmless layout changes.

Prefer user-facing locators when suitable.

Mistake 3: Sharing Test State

One test should not need another test to run first.

Independent tests are easier to rerun and parallelize.

Mistake 4: Putting Credentials in Test Files

Do not commit real usernames, passwords, API keys, or tokens into source code.

Use secure configuration.

Mistake 5: Creating a Huge conftest.py

Fixtures are useful, but keeping all automation logic in a single file can make the code difficult to read and maintain. 

Keep responsibilities clear as the suite grows.

Mistake 6: Testing Too Much in One Test

A test that performs login, search, cart, checkout, profile update, logout, and dozens of assertions can be difficult to diagnose.

Keep tests aligned with meaningful user behavior and risk.

Mistake 7: Copying TypeScript Examples Directly into Python

The concepts are similar, but the syntax is different.

For example:

TypeScript:

await page.getByRole(‘button’, {

  name: ‘Login’

}).click();

Python:

page.get_by_role(

    “button”,

    name=”Login”

).click()

Always use documentation and examples for the language you are actually using.

Playwright Python Best Practices

Write test names that describe expected behavior.

Prefer locators based on roles, labels, and meaningful application semantics.

Use Playwright assertions for conditions that may become true after asynchronous UI updates.

Use fixtures for reusable setup and dependencies.

Keep test data separate when it becomes large enough to deserve independent maintenance.

Store environment details outside individual test files.

Avoid dependencies between tests.

Run important tests against the browser engines required by your users.

Collect useful debugging evidence for CI failures.

Keep dependencies controlled and update them intentionally.

Most importantly, write tests that answer a clear question about application behavior.

Automation code has little value when nobody can understand what the test proves.

A Practical Playwright Python Learning Path

If you are starting with Playwright and Python, learn the topics in this order:

Step 1 — Python basics

Understand functions, classes, lists, dictionaries, loops, imports, exceptions, and basic file handling.

Step 2 — pytest basics

Learn test discovery, assertions, fixtures, markers, and parametrization.

Step 3 — Playwright browser basics

Learn pages, browser contexts, navigation, and user actions.

Step 4 — Locators

Learn role, label, text, test ID, filtering, and locator chaining.

Step 5 — Assertions and waiting

Learn how web-first assertions work and why fixed delays should generally be avoided in automated tests. 

Step 6 — Reusable test design

Learn fixtures and page objects without overengineering the framework.

Step 7 — Debugging

Learn screenshots, traces, headed execution, and failure investigation.

Step 8 — Cross-browser and CI

Run important tests across required browsers and automate execution in CI.

After these skills are comfortable, move into advanced areas such as network interception, API testing, accessibility, data-driven testing, and responsive testing.

Frequently Asked Questions About Playwright Python

Can I use Playwright with Python?

Yes. Playwright provides Python support for browser automation. Python projects can use Playwright directly or combine it with pytest for structured automated testing.

What is pytest-playwright?

pytest-playwright is Playwright’s official pytest plugin, offering browser-testing fixtures and CLI options to simplify writing and running Playwright tests with pytest.

How do I install Playwright with Python?

For a pytest project, install pytest-playwright and then install the required browser binaries with playwright install. Use a virtual environment and a project dependency file for maintainable projects.

What is the page fixture in pytest-playwright?

The page fixture provides a Playwright Page object representing a browser tab. Tests use it for navigation, element interaction, and browser assertions.

Does Playwright Python support Chromium, Firefox and WebKit?

Yes. Playwright can automate Chromium, Firefox, and WebKit through its supported browser builds.

Is pytest required for Playwright Python?

No. Playwright’s Python API can be used without pytest. pytest is commonly used because it provides test discovery, fixtures, parametrization, markers, and a large testing ecosystem.

Should I use Sync or Async Playwright Python?

Choose Sync Playwright Python for simple test automation projects and straightforward test scripts. Use Async Playwright Python when your project benefits from Python’s asynchronous programming model, such as handling multiple tasks concurrently. Both APIs support browser automation, so the best choice depends on your project requirements and code architecture.

Can Playwright Python run tests in parallel?

Yes, Python test suites can use pytest parallel-execution tools such as pytest-xdist. Tests should be isolated before parallel execution is enabled.

Can I use Page Object Model with Playwright Python?

Yes. Python classes can represent reusable page interactions and locators. Use page objects when they improve maintainability rather than creating them for every small page automatically.

Is Playwright Python better than Selenium Python?

Neither answer applies to every project. Playwright provides modern browser automation features such as browser contexts, auto-waiting behavior, tracing, and support for Chromium, Firefox, and WebKit. Selenium has a different ecosystem and broad WebDriver-based support. Choose based on project requirements.

Can Playwright Python be used in CI/CD?

Yes. Playwright Python tests can run in CI systems after Python dependencies and the required browser dependencies are installed.

How should I learn Playwright Python?

Learn Python and pytest fundamentals first, then study Playwright pages, locators, actions, assertions, fixtures, debugging, browser coverage, and CI execution. Add advanced topics after you can build stable basic tests.

Playwright Python: Final Takeaway

Playwright with Python becomes especially useful when Playwright’s browser automation capabilities are combined with pytest’s test organization.

Start with the page fixture.

Use meaningful locators.

Write assertions that verify user-visible behavior.

Use fixtures for reusable setup.

Keep tests independent.

Run important scenarios across the browsers your application supports.

Add parallel execution and CI only after the tests are stable and isolated.

If you are building a larger automation project, continue with the Playwright Framework guide to understand reusable framework structure.

For external datasets, continue with Playwright Data-Driven Testing with JSON, CSV and Excel rather than adding large datasets to this Python tutorial.

For a structured learning path covering Playwright concepts and practical automation skills, explore the Playwright Automation Testing Course in Hyderabad on Playwright Masters.

Playwright Masters automation testing logo - White Back ground

Playwright Masters Team

Playwright Automation Testing Experts | Industry-Focused Training & Practical Learning

Playwright Masters is a dedicated automation testing training platform focused on helping learners build practical skills in Playwright automation testing. Our training covers real-world automation concepts, framework practices, coding, debugging, API testing, cross-browser testing, CI/CD, and interview preparation to help learners develop job-ready testing skills.

Scroll to Top

Start Your Playwright Automation Career Today

Get FREE Demo + Syllabus & Become Job-Ready in Playwright.