Manuals / Playwright with Python / Ch 19

Part 3 · Test Structure & FrameworkIntermediate50 min read

12. Pytest Basics for Playwright

Playwright with Python · 244 pages source format

This is where scripts stop being one-off files and start becoming a real test framework. Everything in this chapter is pytest itself — Playwright plugs into it via pytest-playwright, it doesn't replace it.

What you'll learn

  • This is where scripts stop being one-off files and start becoming a real test fr
  • Everything in this chapter is pytest itself — Playwright plugs into it via pytes
  • Fixtures A fixture is a reusable block of setup (and optional teardown) code tha

Overview

This is where scripts stop being one-off files and start becoming a real test framework. Everything in this chapter is pytest itself — Playwright plugs into it via

Fixtures A fixture is a reusable block of setup (and optional teardown) code that a test can request just by naming it as a parameter. Instead of copy-pasting login steps into every test function, you write it once as a fixture and every test that needs a logged-in user just asks for it. python

Run / study this snippet
import pytest
Interactive study board
OverviewDrag stickies · tap for tips
Study mapDrag stickies · tap for tipsKeep it shortdrag · tap →Name the waitdrag · tap →Scope locatorsdrag · tap →Trace when stuckdrag · tap →One browser firstdrag · tap →Isolate statedrag · tap →Assert the UIdrag · tap →Retry wiselydrag · tap →Seed datadrag · tap →Close the loopdrag · tap →Keep it shortdrag · tap →Name the waitdrag · tap →Scope locatorsdrag · tap →Trace when stuckdrag · tap →One browser firstdrag · tap →Isolate statedrag · tap →Pathwise hackdrag · tap →Page under testdrag · tap →Multi-browserdrag · tap →Automation pathdrag · tap →Tooling nodedrag · tap →
Clear?

page.goto("https://app.example.com/login")

# teardown (runs after the test finishes) — anything after yield

testuser")).to_be_visible()

What it does: Marks a function as a reusable setup/teardown block that tests can request by parameter name. Types/params:

Pointers: Code after yield is teardown — it runs after the test completes (pass or fail), making fixtures the right place for cleanup logic (logging out, deleting test data) rather than scattering try/finally blocks through every test. conftest.py and fixture scope conftest.py is a special pytest file — fixtures defined there are automatically available to every test file in the same folder (and subfolders) without any import statement. This is how shared setup (like a logged_in_page fixture used across dozens of test files) gets centralized in one place instead of duplicated or manually imported everywhere. python

  • scope (string, optional, default "function")
  • "function" — re-runs for every single test that uses it
  • "class" — runs once per test class
  • "module" — runs once per test file
  • "session" — runs once for the entire test run
  • autouse (boolean, optional, default False)
  • True → fixture runs automatically for every applicable test without being explicitly requested
  • False → must be named as a test parameter to be used
  • params (list, optional) — turns the fixture into a parametrized fixture; the test using it runs once per value in the list
Run / study this snippet
# conftest.py
Interactive study board
page.goto("https://app.example.com/login")Drag stickies · tap for tips
Study mapDrag stickies · tap for tipsKeep it shortdrag · tap →Name the waitdrag · tap →Scope locatorsdrag · tap →Trace when stuckdrag · tap →One browser firstdrag · tap →Isolate statedrag · tap →Assert the UIdrag · tap →Retry wiselydrag · tap →Seed datadrag · tap →Close the loopdrag · tap →Keep it shortdrag · tap →Name the waitdrag · tap →Scope locatorsdrag · tap →Trace when stuckdrag · tap →One browser firstdrag · tap →Isolate statedrag · tap →Pathwise hackdrag · tap →Page under testdrag · tap →Multi-browserdrag · tap →Automation pathdrag · tap →Tooling nodedrag · tap →
Clear?

page.goto("https://app.example.com")

Scope choice matters for speed: a session-scoped login fixture (log in once, reuse the saved session for every test — tying into storage_state in Chapter 20) can save enormous amounts of time versus a function-scoped one that logs in fresh before every single test. But scope should match reality — if tests mutate shared state (e.g., one test changes a setting another test depends on being default), a broader scope than function can cause tests to interfere with each other in ways that are painful to debug.

conftest.py (concept, not a function) What it does: A special filename pytest auto-discovers; fixtures defined here are shared across all test files in that directory and below, with no import needed. Types/params: N/A — it's a file location convention, not a callable. Pointers: Put widely-shared fixtures (base URL, login, browser context config) here. Test-file-specific fixtures can stay local to that file instead, to avoid a bloated global conftest.py.

Installing pytest-playwright automatically gives you a page fixture (and browser, context) ready to use in any test, with no setup code required: python

Run / study this snippet
pytest-playwright plugin basics
Interactive study board
page.goto("https://app.example.com")Drag stickies · tap for tips
Study mapDrag stickies · tap for tipsKeep it shortdrag · tap →Name the waitdrag · tap →Scope locatorsdrag · tap →Trace when stuckdrag · tap →One browser firstdrag · tap →Isolate statedrag · tap →Assert the UIdrag · tap →Retry wiselydrag · tap →Seed datadrag · tap →Close the loopdrag · tap →Keep it shortdrag · tap →Name the waitdrag · tap →Scope locatorsdrag · tap →Trace when stuckdrag · tap →One browser firstdrag · tap →Isolate statedrag · tap →Pathwise hackdrag · tap →Page under testdrag · tap →Multi-browserdrag · tap →Automation pathdrag · tap →Tooling nodedrag · tap →
Clear?

expect(page).to_have_title("Example Domain")

The plugin also adds useful command-line flags: bash

Chromium

watching a test run

page, browser, context (pytest-playwright built-in fixtures) What it does: Automatically provided fixtures giving you a ready-to-use Page/Browser/BrowserContext in any test function, without manual setup. Types/params: N/A — request by naming them as test function parameters, e.g. def

Pointers: page is by far the most commonly used — it comes with a fresh BrowserContext per test by default, giving you test isolation automatically (see Part 1, Chapter 3 on the Browser/Context/Page hierarchy).

Run / study this snippet
pytest --headed              # run visibly instead of headless
Interactive study board
expect(page).to_have_title("Example Domain")Drag stickies · tap for tips
Study mapDrag stickies · tap for tipsKeep it shortdrag · tap →Name the waitdrag · tap →Scope locatorsdrag · tap →Trace when stuckdrag · tap →One browser firstdrag · tap →Isolate statedrag · tap →Assert the UIdrag · tap →Retry wiselydrag · tap →Seed datadrag · tap →Close the loopdrag · tap →Keep it shortdrag · tap →Name the waitdrag · tap →Scope locatorsdrag · tap →Trace when stuckdrag · tap →One browser firstdrag · tap →Isolate statedrag · tap →Pathwise hackdrag · tap →Page under testdrag · tap →Multi-browserdrag · tap →Automation pathdrag · tap →Tooling nodedrag · tap →
Clear?

Checklist