Installing Playwright for Python

8 min read

You know Playwright. You know Python. This lesson stitches them together — from an empty folder to a working pytest-playwright project that runs the same tests against Chromium, Firefox, and WebKit. If you've worked through the Python for QA course you already have everything the install needs; if you've worked through the Playwright with TypeScript course the structure here will feel deeply familiar — same ideas, snake_case names, pytest fixtures instead of test() blocks.

Prerequisites

Before you start, you need:

  • Python 3.8 or later. Run python --version (or python3 --version on macOS/Linux). Anything older won't work — Playwright's Python bindings require modern asyncio and type-hint features.
  • pip on the path. Bundled with modern Python installers. Check with pip --version.
  • VS Code. Every example assumes the Microsoft Python extension is installed for type hints, fixture autocomplete, and the integrated test explorer.

If python --version prints 3.8+ and pip --version works, you're ready. We won't re-explain virtual environments, print, or pytest fundamentals — those live in the Python for QA course.

Installing — two pip packages plus the browsers

Unlike npm init playwright@latest from the TypeScript course (which scaffolds the whole project in one go), the Python install is two steps: pip the libraries, then download the browser binaries.

mkdir playwright-python-tests
cd playwright-python-tests
python -m venv .venv
source .venv/bin/activate          # Windows: .venv\Scripts\activate
 
pip install playwright pytest-playwright
playwright install

What each package does:

  • playwright — the core library. Provides sync_playwright, async_playwright, and the Page / Browser / Locator classes.
  • pytest-playwright — the official pytest plugin. Registers the page, browser, context, and playwright fixtures so your tests just declare them as parameters and pytest wires them up.
  • playwright install — downloads Chromium, Firefox, and WebKit binaries to ~/.cache/ms-playwright/. About 200 MB total. Same browsers, same versions as the TypeScript install — Microsoft pins them per Playwright release.

On a Linux CI runner you also need the system libraries each browser links against:

playwright install --with-deps

--with-deps runs apt-get install for libnss3, libatk1.0-0, libxss1, and the rest of the browser dependency tree. Skip it on macOS and Windows — the packages aren't apt-managed there.

Project structure — what to put where

A clean Playwright Python project mirrors the structure you'd see in any pytest codebase, with a few Playwright-specific folders:

playwright-python-tests/
├── tests/
│   ├── conftest.py        ← shared fixtures and config
│   ├── test_login.py      ← test files start with test_
│   └── test_products.py
├── pages/                 ← page objects (chapter 5)
├── fixtures/              ← test data JSON files
├── requirements.txt       ← dependencies
└── pytest.ini             ← pytest configuration

The naming rules pytest enforces:

  • Test files start with test_ (or end with _test).
  • Test functions start with test_.
  • Test classes start with Test.

These are not Playwright conventions — they're pytest's discovery defaults. Break them and pytest silently won't run your tests, which is the most common "why is my test not running?" beginner trap.

Pin your dependencies — requirements.txt

A reproducible install means pinning versions:

playwright==1.44.0
pytest==8.2.0
pytest-playwright==0.5.0

Install from the file with pip install -r requirements.txt. CI does the same — lock to the exact same versions as your dev machine and you eliminate "works on my laptop" mysteries.

pytest.ini — your project's settings file

pytest.ini is where pytest picks up its run-time options. The Playwright-relevant ones:

[pytest]
addopts = --headed --browser chromium
base_url = http://localhost:3000

What each line does:

  • addopts — flags pytest applies on every run. --headed shows the browser window (helpful for development); --browser chromium selects the browser. Drop --headed for CI.
  • base_url — the URL page.goto("/login") is resolved against. The Playwright Python equivalent of baseURL in playwright.config.ts.

You can also set the test directory, custom markers, and warning filters here — same syntax as any other pytest project.

Running your first test — the commands

The flags you'll use every day:

pytest tests/ -v                                  # all tests, verbose output
pytest tests/test_login.py -v                     # one file
pytest tests/ --browser firefox                   # Firefox only
pytest tests/ --browser chromium --browser firefox  # both browsers
pytest tests/ -k "login and not admin"            # name filter
pytest tests/ -m smoke                            # marker filter

The --browser flag is the Python equivalent of playwright.config.ts's projects array — pass it multiple times to fan out across browsers without changing a line of test code.

Headed vs headless mirrors the TypeScript course exactly:

  • Headless (default) — no UI, fast, the right mode for CI.
  • Headed (--headed) — see the browser do the test in real time. Useful while authoring and debugging; remove it from pytest.ini once tests are stable.

The full installation flow

Step 1 of 5

Create the project

mkdir + cd + python -m venv .venv to create an isolated virtualenv. Activate it so pip installs land in the project, not your global Python.

Coming from Playwright TypeScript?

The mapping is deeply direct — same engine, Pythonic naming:

  • playwright.config.tspytest.ini plus conftest.py
  • test('name', async ({ page }) => { ... })def test_name(page: Page):
  • await page.goto('/login')page.goto("/login") (sync API)
  • npx playwright testpytest
  • npx playwright test --uipytest --headed (no full UI mode in Python yet — use --headed plus the Inspector)
  • npx playwright codegenplaywright codegen --target python-pytest
  • getByRole, toBeVisible, toHaveTextget_by_role, to_be_visible, to_have_text

The biggest mental shift is no more await in the typical test. Python's sync API lets pytest-playwright give you the same auto-waiting behaviour as the TypeScript runner without async syntax bleeding into every line. We'll cover sync vs async in the next lesson.

While the install is fresh:

  • Python (ms-python.python) — Microsoft's official extension. Picks up your virtualenv, runs pytest from the Test Explorer, surfaces fixture types in autocomplete.
  • Pylance (ms-python.vscode-pylance) — fast type checker. Lights up page: Page autocomplete on every Playwright method.
  • Black Formatter (ms-python.black-formatter) — auto-formats Python on save.

The full reference is on the Playwright tools page; pytest-specific patterns live in the Python for QA reference.

⚠️ Common mistakes

  • Forgetting playwright install after pip install. The pip package contains the bindings but no browsers. Running pytest without playwright install first throws playwright._impl._errors.Error: Executable doesn't exist. On CI, run playwright install --with-deps in the same job that installs the requirements — never assume the runner has them cached.
  • Naming a test file login_test.py (Java/Go style) instead of test_login.py. pytest discovers files matching test_*.py or *_test.py. The first form is far more common in the Python world; if your test file isn't picked up, this is the first thing to check. Same rule applies to functions: def test_login(...) is collected, def login_test(...) is silently skipped.
  • Mixing the sync and async APIs in one test. pytest-playwright exposes the sync page fixture. If you import async_playwright or sprinkle await in your tests, you get a confusing RuntimeError: This event loop is already running. Pick one API per project — sync for 99% of QA suites, as covered in the next lesson.

🎯 Practice task

Stand up the project you'll use for the rest of the course. 25-30 minutes.

  1. Create a folder playwright-python-tests/. Inside it, run python -m venv .venv and activate the virtualenv.

  2. Run pip install playwright pytest-playwright then playwright install. Confirm the install with playwright --version (you should see something like Version 1.44.0).

  3. Create requirements.txt:

    playwright==1.44.0
    pytest==8.2.0
    pytest-playwright==0.5.0
    
  4. Create pytest.ini:

    [pytest]
    addopts = --headed --browser chromium
    base_url = https://www.saucedemo.com
  5. Create tests/test_smoke.py with a single sanity test:

    from playwright.sync_api import Page, expect
     
    def test_saucedemo_loads(page: Page):
        page.goto("/")
        expect(page.get_by_placeholder("Username")).to_be_visible()
  6. Run pytest tests/ -v. A Chromium window opens, the Sauce Demo login page loads, and pytest reports 1 passed.

  7. Re-run with pytest tests/ --browser firefox -v. The same test runs in Firefox. Then run it against both: pytest tests/ --browser chromium --browser firefox -v — pytest reports 2 passed (one test, two browsers).

  8. Stretch: drop --headed from pytest.ini and re-run. The test still passes but no browser window appears — that's the headless mode every CI run uses. Add a pages/ and fixtures/ folder to your project tree so it matches the structure shown above; you'll fill them in later chapters.

Once pytest tests/ is green across two browsers headlessly, your Playwright Python install is complete. The next lesson breaks down sync vs async — when each fits, why pytest-playwright defaults to sync, and what async unlocks for the rare cases that need it.

// tip to track lessons you complete and pick up where you left off across devices.