Reusable Login Utility
Login is one of the most repeated actions in UI automation. Instead of writing login steps in every test, create a reusable utility.
from playwright.sync_api import Page
def login(page: Page, username: str, password: str):
page.goto("https://example.com/login")
page.get_by_label("Username").fill(username)
page.get_by_label("Password").fill(password)
page.get_by_role("button", name="Login").click()
page.wait_for_url("**/dashboard")
Navigation Utility
Create a common navigation helper when many tests repeatedly open the same application pages.
def open_dashboard(page):
page.goto("https://example.com/dashboard")
page.wait_for_load_state("domcontentloaded")
def open_orders(page):
page.goto("https://example.com/orders")
page.wait_for_load_state("domcontentloaded")
Wait Utility
Playwright already provides automatic waiting. Prefer locator-based waits instead of unnecessary hard-coded sleeps.
def wait_for_dashboard(page):
page.get_by_role(
"heading",
name="Dashboard"
).wait_for(state="visible")
Screenshot Utility
Create a standard screenshot helper for debugging failed tests and capturing important business states.
from pathlib import Path
def take_screenshot(page, name):
Path("screenshots").mkdir(
parents=True,
exist_ok=True
)
page.screenshot(
path=f"screenshots/{name}.png",
full_page=True
)
File Upload Utility
File upload utilities are useful for profile images, invoices, documents and CSV imports.
def upload_file(page, file_path):
page.get_by_label("Upload File").set_input_files(
file_path
)
File Download Utility
Use Playwright's download event when validating reports, invoices or exported files.
def download_report(page):
with page.expect_download() as download_info:
page.get_by_role(
"button",
name="Download Report"
).click()
download = download_info.value
download.save_as(
f"downloads/{download.suggested_filename}"
)
API Request Utility
API utilities can create test data before the UI test starts. This is often faster than creating the same data through the UI.
def create_customer(request, name, email):
response = request.post(
"/api/customers",
data={
"name": name,
"email": email
}
)
assert response.ok
return response.json()
API Response Utility
Centralize API response validation so that tests use consistent status and payload checks.
def assert_success(response):
assert response.ok
assert 200 <= response.status < 300
def assert_json_contains(response, key):
data = response.json()
assert key in data
Element Interaction Utility
Create small reusable helpers for common interactions such as selecting rows, clicking actions or opening menus.
def click_button(page, button_name):
page.get_by_role(
"button",
name=button_name
).click()
def click_link(page, link_name):
page.get_by_role(
"link",
name=link_name
).click()
Form-Filling Utility
Forms such as registration, checkout and customer creation can use a common form utility.
def fill_customer_form(page, customer):
page.get_by_label("First Name").fill(
customer["first_name"]
)
page.get_by_label("Last Name").fill(
customer["last_name"]
)
page.get_by_label("Email").fill(
customer["email"]
)
page.get_by_role(
"button",
name="Save"
).click()
Assertion Utility
Common business validations can be centralized into reusable assertion functions.
from playwright.sync_api import expect
def assert_order_created(page, order_number):
expect(
page.get_by_text(order_number)
).to_be_visible()
URL and Environment Utility
Keep environment-specific URLs outside test logic.
import os
BASE_URL = os.getenv(
"BASE_URL",
"https://test.example.com"
)
def get_base_url():
return BASE_URL
Browser Context Utility
Browser contexts allow tests to simulate different users, permissions and session configurations.
def create_user_context(browser, storage_state=None):
return browser.new_context(
storage_state=storage_state
)
Date and Time Utility
Centralize date generation for reports, bookings, subscriptions and transaction-related tests.
from datetime import datetime, timedelta
def future_date(days=7):
return (
datetime.now() +
timedelta(days=days)
).strftime("%Y-%m-%d")
Logging Utility
Standardized logging makes CI/CD failures much easier to diagnose.
import logging
logger = logging.getLogger(__name__)
def log_step(message):
logger.info("TEST STEP: %s", message)
Retry / Polling Utility
Polling is useful when an external system processes a request asynchronously.
import time
def wait_until(condition, timeout=30, interval=2):
end_time = time.time() + timeout
while time.time() < end_time:
if condition():
return True
time.sleep(interval)
raise TimeoutError(
"Condition was not satisfied"
)
Storage State Utility
Save authenticated browser state once and reuse it across tests when appropriate.
def save_auth_state(context):
context.storage_state(
path="playwright/.auth/user.json"
)
Trace and Diagnostic Utility
Playwright tracing is extremely useful for debugging failures in CI environments.
def start_trace(context):
context.tracing.start(
screenshots=True,
snapshots=True,
sources=True
)
def stop_trace(context, path):
context.tracing.stop(path=path)
Cleanup Utility
Test data should be removed after tests when the environment requires cleanup.
def delete_customer(request, customer_id):
response = request.delete(
f"/api/customers/{customer_id}"
)
assert response.ok
Composite Business Utility
The most powerful utilities combine multiple low-level actions into one business-level workflow.
def create_order(page, product_name, quantity):
page.get_by_placeholder(
"Search products"
).fill(product_name)
page.get_by_text(product_name).click()
page.get_by_label(
"Quantity"
).fill(str(quantity))
page.get_by_role(
"button",
name="Add to Cart"
).click()
page.get_by_role(
"button",
name="Checkout"
).click()
page.get_by_role(
"button",
name="Place Order"
).click()
Quick Reference
| Utility | Real-Time Purpose |
|---|---|
| Login Utility | Authenticate users |
| Navigation Utility | Open common application pages |
| Wait Utility | Synchronize with UI conditions |
| Screenshot Utility | Capture evidence and failures |
| Upload Utility | Upload documents and files |
| Download Utility | Validate generated reports/files |
| API Utility | Create and validate backend data |
| Form Utility | Fill common application forms |
| Assertion Utility | Centralize business validations |
| Environment Utility | Handle different environments |
| Context Utility | Handle users and browser sessions |
| Date Utility | Generate dynamic test dates |
| Logging Utility | Improve test diagnostics |
| Polling Utility | Wait for asynchronous processing |
| Storage State | Reuse authenticated sessions |
| Tracing Utility | Debug CI failures |
| Cleanup Utility | Remove test-created data |
| Business Utility | Represent complete workflows |
Test Utility Best Practices
- Keep utilities small and focused.
- Do not duplicate the same automation logic across tests.
- Prefer Playwright's built-in auto-waiting for UI synchronization.
- Use API utilities to create test data when appropriate.
- Keep environment configuration outside test logic.
- Use meaningful business-level utility names.
- Keep test data creation and cleanup reusable.
- Capture screenshots and traces when debugging failures.
- Avoid creating utilities for one-off actions that add no value.
- Keep reusable utilities independent from individual test cases.
Real-World Test Example
A well-designed test can combine several utilities while keeping the test itself short and readable.
def test_create_order(page):
login(
page,
"testuser@example.com",
"Password123"
)
open_dashboard(page)
create_order(
page,
"Laptop",
2
)
assert_order_created(
page,
"ORD-10001"
)