Fixtures provide reusable setup, test resources, and cleanup.
Playwright's built-in page fixture is the starting point
for most UI tests.
from playwright.sync_api import Page, expect
def test_login_page(page: Page):
page.goto("https://demo.example.com/login")
expect(page.get_by_role("heading", name="Login")).to_be_visible()
page fixture.
Use built-in fixtures instead of manually creating browser objects for every test. This keeps tests short and isolated.
from playwright.sync_api import expect
def test_customer_profile(page):
page.goto("https://shop.example.com/profile")
expect(page.get_by_role("heading", name="My Profile")).to_be_visible()
def test_new_context(browser):
context = browser.new_context()
try:
page = context.new_page()
page.goto("https://shop.example.com")
expect(page).to_have_title("Shop")
finally:
context.close()
page for normal tests. Use
browser and context when a scenario
requires custom browser state, such as a separate session,
cookies, or permissions.
Custom fixtures package repeated business setup so test cases can focus on what they are verifying.
import pytest
from playwright.sync_api import expect
@pytest.fixture
def customer(page):
page.goto("https://shop.example.com/login")
page.get_by_label("Email").fill("qa@example.com")
page.get_by_label("Password").fill("Password123")
page.get_by_role("button", name="Login").click()
return page
def test_customer_can_open_orders(customer):
customer.get_by_role("link", name="Orders").click()
expect(
customer.get_by_role("heading", name="My Orders")
).to_be_visible()
customer fixture instead of repeating the login steps.
Fixture scope controls how often a fixture is created and reused. Choose the smallest safe scope that matches the resource.
import pytest
@pytest.fixture(scope="function")
def test_customer():
return create_customer()
@pytest.fixture(scope="session")
def app_config():
return load_environment_config()
Use yield when a fixture must perform cleanup after
the test completes.
import pytest
@pytest.fixture
def temporary_customer():
customer = create_customer()
yield customer
delete_customer(customer["id"])
def test_customer_profile(temporary_customer):
assert temporary_customer["email"].endswith("@example.com")
Place shared pytest fixtures in conftest.py so multiple
test modules can use them without importing the fixture explicitly.
import pytest
@pytest.fixture
def logged_in_page(page):
page.goto("https://shop.example.com/login")
page.get_by_label("Email").fill("qa@example.com")
page.get_by_label("Password").fill("Password123")
page.get_by_role("button", name="Login").click()
return page
conftest.py and reuse
the logged_in_page fixture across checkout, orders,
and profile test suites.
Pytest fixtures with yield are the preferred Python
mechanism for setup and teardown. Autouse fixtures can behave
like suite-level hooks by running automatically for each test.
import pytest
@pytest.fixture(autouse=True)
def test_logging():
print("START TEST")
yield
print("END TEST")
def test_home_page(page):
page.goto("https://shop.example.com")
An autouse fixture runs automatically for every test within its scope. Use it for truly universal setup, not for hidden business dependencies.
import pytest
from playwright.sync_api import expect
@pytest.fixture(autouse=True)
def clear_test_state():
reset_test_state()
yield
reset_test_state()
def test_cart_starts_empty(page):
page.goto("https://shop.example.com/cart")
expect(
page.get_by_text("Your cart is empty")
).to_be_visible()
Create test data through an API fixture when UI setup would be slow. Then verify the resulting behavior through the UI.
import pytest
from playwright.sync_api import expect
@pytest.fixture
def customer(api_request_context):
response = api_request_context.post(
"/api/customers",
data={
"email": "fixture@example.com",
"name": "Fixture User"
}
)
response.raise_for_status()
return response.json()
def test_customer_appears_in_ui(page, customer):
page.goto("https://shop.example.com/customers")
expect(
page.get_by_text(customer["name"])
).to_be_visible()
Reuse authenticated browser state for tests that do not need to test the login flow itself. This keeps authenticated tests focused on the behavior they are intended to verify.
import pytest
from playwright.sync_api import expect
@pytest.fixture
def authenticated_page(page):
page.goto("https://shop.example.com/login")
page.get_by_label("Email").fill("qa@example.com")
page.get_by_label("Password").fill("Password123")
page.get_by_role("button", name="Login").click()
return page
def test_checkout_requires_login(authenticated_page):
authenticated_page.goto("https://shop.example.com/checkout")
expect(
authenticated_page.get_by_role("heading", name="Checkout")
).to_be_visible()
Fixtures can construct page objects so test cases remain focused on business behavior rather than page-element setup.
import pytest
from playwright.sync_api import expect
class LoginPage:
def __init__(self, page):
self.page = page
self.email = page.get_by_label("Email")
self.password = page.get_by_label("Password")
self.login_button = page.get_by_role("button", name="Login")
@pytest.fixture
def login_page(page):
return LoginPage(page)
def test_login_screen(login_page):
login_page.page.goto("https://demo.example.com/login")
expect(login_page.login_button).to_be_visible()
LoginPage object
into each authentication test, keeping page-object construction
out of the test code.
Centralize environment URLs and configuration so tests can run against different environments without hard-coding values in individual test cases.
import os
import pytest
from playwright.sync_api import expect
@pytest.fixture(scope="session")
def base_url():
return os.getenv("BASE_URL", "https://qa.example.com")
def test_home_page(page, base_url):
page.goto(base_url)
expect(
page.get_by_role("heading", name="Welcome")
).to_be_visible()
BASE_URL environment variable in CI, without modifying
the test code.
Create a custom browser context when tests need a specific locale, timezone, permissions, viewport, or other browser-level settings.
import pytest
from playwright.sync_api import expect
@pytest.fixture
def indian_context(browser):
context = browser.new_context(
locale="en-IN",
timezone_id="Asia/Kolkata"
)
try:
yield context
finally:
context.close()
def test_localized_checkout(indian_context):
page = indian_context.new_page()
page.goto("https://shop.example.com/checkout")
expect(
page.get_by_text("India")
).to_be_visible()
en-IN locale and Asia/Kolkata
timezone.
Create separate fixtures for customer, manager, and admin identities when permissions and role-based access are part of the product.
import pytest
from playwright.sync_api import expect
@pytest.fixture
def admin_page(page):
login_as(page, "admin@example.com")
return page
@pytest.fixture
def manager_page(page):
login_as(page, "manager@example.com")
return page
@pytest.fixture
def customer_page(page):
login_as(page, "customer@example.com")
return page
def test_admin_can_manage_users(admin_page):
admin_page.goto("https://app.example.com/admin/users")
expect(
admin_page.get_by_role("heading", name="Users")
).to_be_visible()
Cleanup is essential when tests create orders, customers, files, or other persistent records. Use fixture teardown to keep the test environment clean and independent.
import pytest
@pytest.fixture
def test_order():
order = create_order(status="NEW")
yield order
cancel_order(order["id"])
def test_order_can_be_cancelled(test_order):
assert test_order["status"] == "NEW"
cancel_order(test_order["id"])
Fixtures compose naturally. A test can receive several independent resources without manually coordinating their setup or cleanup.
import pytest
from playwright.sync_api import expect
def test_customer_can_download_invoice(
authenticated_page,
test_order,
invoice_page
):
authenticated_page.goto(f"/orders/{test_order['id']}")
invoice_page.download_latest_invoice()
expect(
invoice_page.success_message
).to_be_visible()
For end-to-end scenarios, use backend setup for reliable and fast data creation, then use Playwright for customer-facing verification.
import pytest
from playwright.sync_api import expect
@pytest.fixture
def paid_order(api_request_context):
return create_paid_order(api_request_context)
def test_paid_order_is_visible_in_history(
authenticated_page,
paid_order
):
authenticated_page.goto("/orders")
expect(
authenticated_page.get_by_text(paid_order["number"])
).to_be_visible()
A fixture can centralize diagnostics such as screenshots, logging, or other failure artifacts, while Playwright tracing remains available for deeper debugging.
import pytest
@pytest.fixture
def diagnostic_page(page, request):
yield page
if hasattr(request.node, "rep_call") and request.node.rep_call.failed:
page.screenshot(
path=f"artifacts/{request.node.name}.png",
full_page=True
)
@pytest.hookimpl(hookwrapper=True)
def pytest_runtest_makereport(item, call):
outcome = yield
report = outcome.get_result()
setattr(item, f"rep_{report.when}", report)
In Python pytest, fixtures are the primary mechanism for setup and teardown. Treat fixtures as explicit dependencies whenever possible, and use autouse fixtures only for genuinely global behavior.
import pytest
# Explicit dependency — preferred
def test_cart(logged_in_page):
...
# Automatic behavior — use selectively
@pytest.fixture(autouse=True)
def reset_state():
reset_state_before_test()
yield
reset_state_after_test()
autouse fixture.
These patterns cover common enterprise Playwright Python needs, including authentication, API data, cleanup, user roles, environment configuration, browser contexts, page objects, diagnostics, and reusable business workflows.
# 1. Login fixture
# Reuse authentication setup across tests.
# 2. API test-data fixture
# Create test data quickly through backend APIs.
# 3. Cleanup fixture
# Remove temporary data after each test.
# 4. Admin/customer role fixtures
# Test permissions with different user identities.
# 5. Environment fixture
# Centralize URLs and environment-specific settings.
# 6. Browser-context fixture
# Configure locale, timezone, permissions, or viewport.
# 7. Page-object fixture
# Provide ready-to-use page objects to tests.
# 8. Authenticated-state fixture
# Reuse saved authentication state when appropriate.
# 9. Diagnostic fixture
# Capture screenshots, logs, or other failure artifacts.
# 10. Composite business-workflow fixture
# Combine multiple fixtures for reusable business scenarios.