Playwright for Python is an official Microsoft library that brings the full power of Playwright to the Python ecosystem. With 1.4 million+ weekly PyPI downloads in 2026, it's the fastest-growing Python test automation library — and for good reason: auto-waiting, built-in tracing, cross-browser support, and first-class pytest integration make it dramatically better than Selenium for new Python projects.
This tutorial assumes you know basic Python. No prior test automation experience needed.
Why Playwright + Python?
What you get with Playwright Python
- Cross-browser: Chromium, Firefox, WebKit
- Auto-waiting on every action (no sleeps)
- Built-in pytest fixtures (page, browser, context)
- Trace Viewer for visual debugging
- Network interception & mocking
- Screenshot & video on failure
- Sync and async API variants
- Parallel test execution via pytest-xdist
If you're coming from Selenium Python, Playwright eliminates the need for WebDriverWait, ChromeDriverManager, and most explicit waits. Every Playwright action automatically waits for the element to be actionable before interacting with it.
Step 1: Install Playwright for Python
Two commands and you're ready:
# Install Playwright + pytest plugin pip install pytest-playwright # Download browser binaries (Chromium, Firefox, WebKit) playwright install
Virtual environment recommended: Use python -m venv .venv and activate it before installing. This keeps Playwright isolated from your system Python.
Verify the installation:
# Check Playwright version playwright --version # Version 1.48.0 # Check pytest recognizes the plugin pytest --co # Should list available fixtures including 'page', 'browser', 'context'
Step 2: Project Structure
Create a clean project layout:
my-playwright-project/ ├── tests/ │ ├── __init__.py │ ├── test_login.py │ └── test_homepage.py ├── pages/ # Page Object Model (optional) │ ├── __init__.py │ └── login_page.py ├── conftest.py # Shared fixtures ├── pytest.ini # pytest config └── requirements.txt
[pytest] # Run tests in headed mode during development addopts = --headed --slowmo 500 # For CI, remove --headed and --slowmo: # addopts = --browser chromium
Step 3: Write Your First Test
The pytest-playwright plugin provides a page fixture automatically — no browser setup code needed:
import re from playwright.sync_api import Page, expect def test_homepage_has_title(page: Page): """Verify the homepage loads with the correct title.""" page.goto("https://playwright.dev/") # Assert the page title contains "Playwright" expect(page).to_have_title(re.compile("Playwright")) def test_get_started_link(page: Page): """Click 'Get Started' and verify navigation.""" page.goto("https://playwright.dev/") # Click the "Get started" link page.get_by_role("link", name="Get started").click() # Verify navigation to the intro page expect(page.get_by_role("heading", name="Installation")).to_be_visible()
Run it:
# Run all tests pytest # Run a specific file pytest tests/test_homepage.py # Run with verbose output pytest -v
Step 4: Locator Strategies
Playwright Python uses the same role-based locators as the TypeScript version. Always prefer these over CSS/XPath — they're more resilient to UI changes:
# ✅ BEST: Role-based locators (accessible, resilient) page.get_by_role("button", name="Submit") page.get_by_role("link", name="Sign In") page.get_by_role("heading", name="Dashboard") # ✅ GOOD: Label and placeholder page.get_by_label("Email address") page.get_by_placeholder("Search...") # ✅ GOOD: Test ID (requires data-testid in HTML) page.get_by_test_id("checkout-button") # ✅ OK: Text content page.get_by_text("Add to Cart") # ⚠️ AVOID: CSS selectors (brittle) page.locator("#submit-btn") page.locator(".form-container > button.primary")
Step 5: Assertions
Playwright's expect API auto-waits for conditions to be true (with a default 5-second timeout):
from playwright.sync_api import expect # Page-level assertions expect(page).to_have_url("https://example.com/dashboard") expect(page).to_have_title("Dashboard") # Element visibility expect(page.get_by_role("alert")).to_be_visible() expect(page.get_by_text("Loading")).to_be_hidden() # Element content expect(page.get_by_test_id("count")).to_have_text("42") expect(page.get_by_role("textbox")).to_have_value("user@test.com") # Element state expect(page.get_by_role("button", name="Submit")).to_be_enabled() expect(page.get_by_role("checkbox")).to_be_checked()
Step 6: A Real-World Login Test
from playwright.sync_api import Page, expect def test_successful_login(page: Page): """User can log in with valid credentials.""" page.goto("http://localhost:3000/login") page.get_by_label("Email").fill("user@example.com") page.get_by_label("Password").fill("securePass123") page.get_by_role("button", name="Sign In").click() # Verify redirect to dashboard expect(page).to_have_url("http://localhost:3000/dashboard") expect(page.get_by_role("heading", name="Dashboard")).to_be_visible() def test_invalid_credentials_show_error(page: Page): """Invalid login shows an error message.""" page.goto("http://localhost:3000/login") page.get_by_label("Email").fill("wrong@example.com") page.get_by_label("Password").fill("wrongpass") page.get_by_role("button", name="Sign In").click() # Error message should appear expect(page.get_by_role("alert")).to_have_text("Invalid email or password") # Should stay on login page expect(page).to_have_url("http://localhost:3000/login")
Step 7: Shared Fixtures with conftest.py
Use conftest.py to set base URL, default timeouts, and shared authentication:
import pytest from playwright.sync_api import Page @pytest.fixture(scope="session") def browser_context_args(browser_context_args): """Set default viewport and locale for all tests.""" return { **browser_context_args, "viewport": {"width": 1280, "height": 720}, "locale": "en-US", } @pytest.fixture(scope="session") def base_url(): """Base URL for all page.goto() calls.""" return "http://localhost:3000"
Authentication fixture: For apps behind login, save auth state once and reuse it. Run playwright codegen --save-storage=auth.json, log in manually, then load the state in conftest: browser_context_args["storage_state"] = "auth.json".
Step 8: Page Object Model
For larger test suites, encapsulate page interactions in classes:
from playwright.sync_api import Page, expect 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.submit = page.get_by_role("button", name="Sign In") self.error = page.get_by_role("alert") def goto(self): self.page.goto("/login") return self def login(self, email: str, password: str): self.email.fill(email) self.password.fill(password) self.submit.click() def expect_error(self, message: str): expect(self.error).to_have_text(message)
from pages.login_page import LoginPage def test_successful_login(page): login = LoginPage(page).goto() login.login("user@example.com", "securePass123") expect(page).to_have_url("/dashboard")
Step 9: Tracing & Debugging
Playwright's Trace Viewer is the most powerful debugging tool in test automation. Enable it on failure:
# Record traces only on test failure pytest --tracing retain-on-failure # View the trace file playwright show-trace test-results/test-login-chromium/trace.zip
The Trace Viewer shows a timeline of every action, DOM snapshots before and after each step, network requests, and console logs — making it trivial to understand why a test failed.
Step 10: Cross-Browser Testing
# Run on all browsers pytest --browser chromium --browser firefox --browser webkit # Run on a specific browser pytest --browser firefox # Run headed (visible browser window) pytest --headed # Slow motion (great for demos) pytest --headed --slowmo 1000
Step 11: Parallel Execution
Install pytest-xdist for parallel test execution:
pip install pytest-xdist # Run tests across 4 workers pytest -n 4 # Auto-detect number of CPU cores pytest -n auto
Step 12: CI/CD with GitHub Actions
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" - run: pip install pytest-playwright - run: playwright install --with-deps chromium - run: pytest --browser chromium --tracing retain-on-failure - uses: actions/upload-artifact@v4 if: always() with: name: playwright-traces path: test-results/
Playwright Python vs Selenium Python
If you're deciding between the two for a Python project in 2026:
- Auto-waiting: Playwright auto-waits on every action. Selenium requires explicit
WebDriverWaiteverywhere. - Setup: Playwright is 2 commands (
pip install+playwright install). Selenium needs browser drivers + driver managers. - Speed: Playwright uses direct browser protocols. Selenium goes through WebDriver HTTP bridge (slower).
- Debugging: Playwright has Trace Viewer. Selenium has manual logging.
- Cross-browser: Playwright bundles exact browser versions. Selenium depends on system-installed drivers.
- AI integration: Playwright has MCP Server for Claude AI test generation. Selenium has nothing comparable.
For a full comparison, see our Playwright vs Cypress vs Selenium 2026 guide.
Frequently Asked Questions
Can I use Playwright with Python?
Yes. Playwright has an official Python library maintained by Microsoft. Install with pip install pytest-playwright and playwright install. It supports all the same features as the TypeScript version.
Is Playwright Python as good as Playwright TypeScript?
Yes — identical capabilities. Both are maintained by Microsoft. The Python API has sync and async variants. All locators, tracing, network interception, and browser support work the same way.
How do I install Playwright for Python?
Run pip install pytest-playwright then playwright install. Two commands, under 60 seconds.
Should I use pytest or unittest with Playwright?
Use pytest. The official pytest-playwright plugin provides built-in fixtures, automatic browser lifecycle management, parallel execution, and screenshot-on-failure out of the box.
Is Playwright Python better than Selenium Python?
For new projects in 2026, yes. Playwright is faster, has auto-waiting, includes tracing and video recording, and supports all modern browsers. Selenium is only better if you need IE11 or use Ruby/PHP elsewhere in your org.
Asim Noaman
Senior QA Automation Engineer & AI Testing Specialist
With years of hands-on experience building test automation frameworks for production applications, Asim specializes in combining traditional QA methodologies with cutting-edge AI tools. He has helped teams adopt Playwright and AI-driven testing workflows to ship faster with fewer bugs.