Skip to content

🌟 A robust Python Pytest automation framework using Playwright for UI testing. Includes CI/CD configurations for GitHub Actions, Jenkins, and AWS.

License

Notifications You must be signed in to change notification settings

amitbad/Python-Playwright-Pytest

Repository files navigation

Playwright Python Test Automation Framework

A comprehensive, production-ready test automation framework built with Playwright, Python 3.11+, and Pytest. This framework supports both UI and REST API testing with proper error handling, reporting, and CI/CD integration.

Table of Contents


Features

  • Playwright + Python: Modern browser automation with Python 3.11+
  • Pytest Integration: Powerful test framework with fixtures and markers
  • Page Object Model: Clean separation of test logic and page interactions
  • JSON-Based Locators: Easy-to-maintain locator storage
  • REST API Testing: Built-in API client with retry mechanism
  • Multiple Browsers: Support for Chromium, Firefox, and WebKit
  • Session Management: Login once, reuse across tests
  • Attach to Browser: Connect to existing browser for debugging
  • Screenshot Capture: Automatic screenshots on failure
  • Allure Reporting: Beautiful, interactive test reports
  • Email Notifications: Send reports via email
  • CI/CD Ready: GitHub Actions, Jenkins, Azure DevOps, AWS CodePipeline

Project Structure

Python-Playwright-Pytest/
β”œβ”€β”€ config/                     # Configuration management
β”‚   β”œβ”€β”€ __init__.py
β”‚   └── settings.py            # Pydantic settings
β”œβ”€β”€ locators/                   # JSON-based locators
β”‚   β”œβ”€β”€ __init__.py
β”‚   β”œβ”€β”€ locator_manager.py     # Locator management
β”‚   └── pages/                 # Page-specific locators
β”‚       β”œβ”€β”€ login_page.json
β”‚       └── home_page.json
β”œβ”€β”€ pages/                      # Page Object Model
β”‚   β”œβ”€β”€ __init__.py
β”‚   β”œβ”€β”€ base_page.py           # Base page with common methods
β”‚   β”œβ”€β”€ login_page.py
β”‚   └── home_page.py
β”œβ”€β”€ tests/                      # Test files
β”‚   β”œβ”€β”€ __init__.py
β”‚   β”œβ”€β”€ ui/                    # UI tests
β”‚   β”‚   β”œβ”€β”€ __init__.py
β”‚   β”‚   β”œβ”€β”€ test_login.py
β”‚   β”‚   └── test_home.py
β”‚   └── api/                   # API tests
β”‚       β”œβ”€β”€ __init__.py
β”‚       └── test_api_example.py
β”œβ”€β”€ utils/                      # Utility classes
β”‚   β”œβ”€β”€ __init__.py
β”‚   β”œβ”€β”€ api_client.py          # REST API client
β”‚   β”œβ”€β”€ screenshot_manager.py  # Screenshot handling
β”‚   β”œβ”€β”€ email_reporter.py      # Email notifications
β”‚   └── logger.py              # Logging configuration
β”œβ”€β”€ reports/                    # Test reports (generated)
β”œβ”€β”€ screenshots/                # Screenshots (generated)
β”œβ”€β”€ traces/                     # Playwright traces (generated)
β”œβ”€β”€ logs/                       # Log files (generated)
β”œβ”€β”€ .github/workflows/          # GitHub Actions
β”‚   └── test.yml
β”œβ”€β”€ aws-codepipeline/          # AWS CodePipeline
β”‚   └── buildspec.yml
β”œβ”€β”€ conftest.py                # Pytest fixtures
β”œβ”€β”€ pytest.ini                 # Pytest configuration
β”œβ”€β”€ requirements.txt           # Python dependencies
β”œβ”€β”€ .env.example              # Environment template
β”œβ”€β”€ .gitignore
β”œβ”€β”€ Jenkinsfile               # Jenkins pipeline
β”œβ”€β”€ azure-pipelines.yml       # Azure DevOps pipeline
└── README.md

Prerequisites

  • Python 3.11.5 (or higher)
  • pip (Python package manager)
  • Git (for version control)
  • Node.js (required by Playwright)

Verify Python Installation

python3 --version
# Should output: Python 3.11.5 or higher

Installation

Step 1: Clone Repository

git clone https://github.com/amitbad/Python-Playwright-Pytest.git
cd Python-Playwright-Pytest

Step 2: Create Virtual Environment

Creating a virtual environment isolates project dependencies from your system Python.

# Create virtual environment
python3 -m venv venv

# Activate virtual environment
# On macOS/Linux:
source venv/bin/activate

# On Windows:
# venv\Scripts\activate

# Verify activation (should show venv path)
which python

To deactivate the virtual environment:

deactivate

Step 3: Install Dependencies

# Upgrade pip first
pip install --upgrade pip

# Install all dependencies
pip install -r requirements.txt

Step 4: Install Playwright Browsers

Playwright requires browser binaries to be installed:

# Install all browsers
playwright install

# Or install specific browser
playwright install chromium
playwright install firefox
playwright install webkit

# Install with system dependencies (recommended for CI)
playwright install --with-deps chromium

Step 5: Configure Environment

# Copy the example environment file
cp .env.example .env

# Edit .env with your configuration
nano .env  # or use any text editor

Important .env variables:

# Application URLs
BASE_URL=https://your-app.com
API_BASE_URL=https://api.your-app.com

# Test Credentials
TEST_USERNAME=your_test_user@example.com
TEST_PASSWORD=your_secure_password

# Browser Settings
BROWSER=chromium
HEADLESS=true

Running Tests

Run All Tests

# Run all tests
pytest

# Run with verbose output
pytest -v

# Run with detailed output
pytest -v --tb=long

Run by Markers

# Run smoke tests only
pytest -m smoke

# Run regression tests
pytest -m regression

# Run API tests only
pytest -m api

# Run UI tests only
pytest -m ui

# Run tests requiring login
pytest -m login_required

# Combine markers
pytest -m "smoke and ui"
pytest -m "not slow"

Run Specific Tests

# Run specific test file
pytest tests/ui/test_login.py

# Run specific test class
pytest tests/ui/test_login.py::TestLogin

# Run specific test method
pytest tests/ui/test_login.py::TestLogin::test_successful_login

# Run tests matching pattern
pytest -k "login"
pytest -k "test_get"

Run with Different Browsers

# Run with Firefox
pytest --browser firefox

# Run with WebKit (Safari)
pytest --browser webkit

# Run with Chromium (default)
pytest --browser chromium

Run in Headed Mode

# Run with visible browser
pytest --headed

# Run with slow motion (for debugging)
SLOW_MO=500 pytest --headed

Parallel Execution

# Install pytest-xdist
pip install pytest-xdist

# Run tests in parallel
pytest -n auto  # Auto-detect CPU count
pytest -n 4     # Use 4 workers

Locator Management

Locators are stored in JSON files for easy maintenance. This approach:

  • Separates locators from test code
  • Allows non-developers to update locators
  • Makes locators version-control friendly

JSON Locator Format

// locators/pages/login_page.json
{
  "username_input": {
    "selector": "#username",
    "type": "css",
    "description": "Username input field"
  },
  "password_input": {
    "selector": "#password",
    "type": "css",
    "description": "Password input field"
  },
  "login_button": {
    "selector": "button[type='submit']",
    "type": "css",
    "description": "Login submit button"
  }
}

Supported Locator Types

Type Description Example
css CSS selector #id, .class, [attr='value']
xpath XPath selector //button[@type='submit']
text Text content Login, Submit
role ARIA role button:Login
testid Test ID attribute login-button
label Label text Username
placeholder Placeholder text Enter username

Using Locators in Page Objects

from pages.base_page import BasePage

class LoginPage(BasePage):
    PAGE_NAME = "login_page"  # Matches JSON filename
    
    def enter_username(self, username: str):
        # Uses locator from login_page.json
        self.fill("username_input", username)

Page Object Model

The framework uses Page Object Model (POM) for maintainable tests.

Creating a New Page Object

  1. Create locator JSON file:
// locators/pages/my_page.json
{
  "element_name": {
    "selector": "#my-element",
    "type": "css",
    "description": "Description"
  }
}
  1. Create page class:
# pages/my_page.py
from pages.base_page import BasePage

class MyPage(BasePage):
    PAGE_NAME = "my_page"  # Must match JSON filename
    PAGE_URL = "/my-page"
    
    def do_something(self):
        self.click("element_name")
        self.fill("input_field", "text")
  1. Use in tests:
def test_my_feature(page):
    my_page = MyPage(page)
    my_page.navigate()
    my_page.do_something()

Fixtures

Available Fixtures

Fixture Scope Description
settings session Application settings
browser session Browser instance
context function Browser context (isolated)
page function New page for each test
authenticated_context session Context with login session
authenticated_page function Page with authentication
api_client session REST API client
screenshot_manager session Screenshot utility

Session-Scoped Login/Logout

The framework supports login once, use everywhere pattern:

# In conftest.py - Login is performed once per session
@pytest.fixture(scope="session")
def authenticated_context(browser, settings):
    context = browser.new_context()
    page = context.new_page()
    
    # Login once
    login_page = LoginPage(page)
    login_page.navigate_to_login()
    login_page.login_with_credentials()
    
    page.close()
    yield context
    
    # Logout once at the end
    logout_page = context.new_page()
    HomePage(logout_page).logout()
    context.close()

# Use in tests
def test_something(authenticated_page):
    # Already logged in!
    home_page = HomePage(authenticated_page)
    home_page.do_something()

Benefits:

  • Login/logout executed only once per test session
  • Faster test execution
  • Shared authentication state across tests

API Testing

The framework includes a robust API client for REST API testing.

Basic Usage

from utils.api_client import APIClient

def test_api_example(api_client):
    # GET request
    response = api_client.get("/users")
    assert response.status_code == 200
    
    # POST request
    response = api_client.post("/users", json={
        "name": "John",
        "email": "john@example.com"
    })
    assert response.status_code == 201
    
    # PUT request
    response = api_client.put("/users/1", json={"name": "Updated"})
    
    # DELETE request
    response = api_client.delete("/users/1")

With Authentication

def test_authenticated_api(api_client):
    # Set token
    api_client.token = "your-jwt-token"
    
    # Or set API key
    api_client.set_api_key("your-api-key")
    
    response = api_client.get("/protected-resource")

Attach to Running Browser

You can attach tests to an already running browser for debugging. This saves time during development.

Step 1: Launch Browser with Remote Debugging

# macOS - Chrome
/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome \
  --remote-debugging-port=9222 \
  --user-data-dir=/tmp/chrome-debug

# macOS - Chromium
/Applications/Chromium.app/Contents/MacOS/Chromium \
  --remote-debugging-port=9222 \
  --user-data-dir=/tmp/chromium-debug

# Linux - Chrome
google-chrome --remote-debugging-port=9222 --user-data-dir=/tmp/chrome-debug

# Windows - Chrome
"C:\Program Files\Google\Chrome\Application\chrome.exe" ^
  --remote-debugging-port=9222 ^
  --user-data-dir=C:\temp\chrome-debug

Step 2: Get CDP Endpoint

Open http://localhost:9222/json/version in another browser and copy the webSocketDebuggerUrl.

Step 3: Configure Framework

Add to your .env file:

CDP_ENDPOINT=ws://localhost:9222/devtools/browser/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx

Step 4: Run Tests

pytest tests/ui/test_login.py -v

The tests will now use your existing browser session!


Screenshots

Automatic Screenshots on Failure

Screenshots are automatically captured when tests fail. Configure in .env:

SCREENSHOT_ON_FAILURE=true

Screenshots are saved to screenshots/failures/ and attached to Allure reports.

Manual Screenshots

def test_with_screenshot(page):
    # In page object
    my_page.take_screenshot("step_1")
    
    # Full page screenshot
    my_page.take_screenshot("full_page", full_page=True)
    
    # Element screenshot
    my_page.take_element_screenshot("element_name", "element_shot")

Reporting

Default HTML Report

HTML reports are generated automatically:

# Run tests (report generated automatically)
pytest

# Report location
open reports/report.html

Allure Reports

Allure provides beautiful, interactive reports.

Installing Allure

macOS (using Homebrew)

# Install Homebrew if not installed
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

# Install Allure
brew install allure

# Verify installation
allure --version

Linux

# Download and install
wget https://github.com/allure-framework/allure2/releases/download/2.24.0/allure-2.24.0.tgz
tar -xzf allure-2.24.0.tgz
sudo mv allure-2.24.0 /opt/allure
sudo ln -s /opt/allure/bin/allure /usr/local/bin/allure

# Verify
allure --version

Windows

# Using Scoop
scoop install allure

# Or using Chocolatey
choco install allure

# Verify
allure --version

Generate Allure Report

# Run tests (generates allure-results)
pytest --alluredir=reports/allure-results

# Generate HTML report
allure generate reports/allure-results -o reports/allure-report --clean

# Open report in browser
allure open reports/allure-report

# Or serve directly
allure serve reports/allure-results

Email Reports

Configure email settings in .env:

SMTP_SERVER=smtp.gmail.com
SMTP_PORT=587
SMTP_USERNAME=your_email@gmail.com
SMTP_PASSWORD=your_app_password
EMAIL_RECIPIENTS=recipient1@example.com,recipient2@example.com

Gmail App Password Setup

  1. Go to Google Account β†’ Security
  2. Enable 2-Step Verification
  3. Go to App passwords
  4. Generate a new app password for "Mail"
  5. Use this password in SMTP_PASSWORD

Send Report Manually

from utils.email_reporter import EmailReporter

reporter = EmailReporter()
reporter.send_report(
    test_results={
        "passed": 10,
        "failed": 2,
        "total": 12,
        "duration": "5m 30s"
    },
    attachments=["reports/report.html"]
)

Automatic Email After Tests

Uncomment in conftest.py:

def pytest_sessionfinish(session, exitstatus):
    reporter = EmailReporter()
    reporter.send_report(
        test_results={...},
        attachments=["reports/report.html"]
    )

CI/CD Integration

GitHub Actions

The workflow is configured for manual trigger only to prevent automatic runs.

Setup Steps

  1. Add Secrets in GitHub Repository:

    • Go to Settings β†’ Secrets and variables β†’ Actions
    • Add these secrets:
      • BASE_URL: Your application URL
      • TEST_USERNAME: Test user username
      • TEST_PASSWORD: Test user password
      • API_KEY: API key (if needed)
  2. Run Workflow Manually:

    • Go to Actions tab
    • Select "Test Automation" workflow
    • Click "Run workflow"
    • Select branch and options
    • Click "Run workflow"
  3. Enable Automatic Triggers (optional): Edit .github/workflows/test.yml and uncomment:

    push:
      branches: [ main, develop ]
    pull_request:
      branches: [ main ]

View Results

  • Go to Actions tab
  • Click on the workflow run
  • Download artifacts (test-results, allure-results)

Jenkins

Prerequisites

  • Jenkins with Pipeline plugin
  • Python 3.11+ on Jenkins agent
  • Allure Jenkins plugin (optional)

Setup Steps

  1. Create Pipeline Job:

    • New Item β†’ Pipeline
    • Name: "Test Automation"
  2. Configure Pipeline:

    • Definition: Pipeline script from SCM
    • SCM: Git
    • Repository URL: Your repo URL
    • Script Path: Jenkinsfile
  3. Add Credentials:

    • Manage Jenkins β†’ Credentials
    • Add credentials:
      • TEST_USERNAME: Secret text
      • TEST_PASSWORD: Secret text
      • API_KEY: Secret text
  4. Install Allure Plugin (optional):

    • Manage Jenkins β†’ Plugins
    • Search and install "Allure"
    • Configure Allure in Global Tool Configuration
  5. Run Pipeline:

    • Open the job
    • Click "Build with Parameters"
    • Select browser and test type
    • Click "Build"

View Results

  • Build page shows test results
  • Allure Report link (if plugin installed)
  • Download artifacts from build

Azure DevOps

Setup Steps

  1. Create Pipeline:

    • Pipelines β†’ New Pipeline
    • Select your repository
    • Choose "Existing Azure Pipelines YAML file"
    • Select azure-pipelines.yml
  2. Configure Variables:

    • Edit Pipeline β†’ Variables
    • Add variables:
      • BASE_URL: Your application URL
      • TEST_USERNAME: (mark as secret)
      • TEST_PASSWORD: (mark as secret)
      • API_KEY: (mark as secret)
  3. Run Pipeline:

    • Click "Run pipeline"
    • Select parameters (browser, test type)
    • Click "Run"

View Results

  • Pipeline run shows test results
  • Download artifacts from pipeline
  • View test results in "Tests" tab

AWS CodePipeline

Setup Steps

  1. Create CodeBuild Project:

    • Go to AWS CodeBuild
    • Create build project
    • Source: Your repository (GitHub, CodeCommit)
    • Environment:
      • Managed image
      • Ubuntu, Standard, aws/codebuild/standard:7.0
    • Buildspec: aws-codepipeline/buildspec.yml
  2. Configure Environment Variables:

    • In CodeBuild project settings
    • Add environment variables:
      • BASE_URL
      • TEST_USERNAME
      • TEST_PASSWORD
      • API_KEY
    • Or use AWS Secrets Manager
  3. Create CodePipeline (optional):

    • Create pipeline
    • Add Source stage
    • Add Build stage with CodeBuild project
  4. Run Build:

    • Start build manually
    • Or trigger via pipeline

View Results

  • Build logs in CodeBuild
  • Artifacts in S3 (if configured)
  • Test reports in CodeBuild Reports

Best Practices

Test Organization

  1. Use markers to categorize tests:

    @pytest.mark.smoke
    @pytest.mark.ui
    def test_login():
        pass
  2. Use Allure decorators for better reports:

    @allure.epic("Authentication")
    @allure.feature("Login")
    @allure.severity(allure.severity_level.CRITICAL)
    def test_login():
        pass
  3. Use steps for clarity:

    with allure.step("Navigate to login page"):
        login_page.navigate()

Locator Best Practices

  1. Prefer stable selectors:

    • Test IDs: [data-testid='login-btn']
    • IDs: #login-button
    • Avoid: XPath with indexes, dynamic classes
  2. Keep locators updated:

    • Review locators when UI changes
    • Use descriptive names

Performance Tips

  1. Use session-scoped fixtures for expensive setup
  2. Run tests in parallel with pytest-xdist
  3. Use headless mode in CI/CD
  4. Attach to browser during development

Troubleshooting

Common Issues

Browser not found

# Reinstall browsers
playwright install --with-deps

Permission denied on macOS

# Allow Playwright to access browsers
xattr -cr ~/.cache/ms-playwright

Tests timeout

# Increase timeout in .env
DEFAULT_TIMEOUT=60000

CDP connection failed

# Ensure browser is running with correct port
# Check if port 9222 is in use
lsof -i :9222

Debug Mode

# Run with debug logging
DEBUG=true pytest -v

# Run with Playwright debug
PWDEBUG=1 pytest tests/ui/test_login.py

Contributing

  1. Fork the repository
  2. Create a feature branch
  3. Make your changes
  4. Run tests locally
  5. Submit a pull request

Support

For issues and questions:

  • Create an issue in the repository
  • Check existing issues for solutions

Happy Testing! πŸš€

About

🌟 A robust Python Pytest automation framework using Playwright for UI testing. Includes CI/CD configurations for GitHub Actions, Jenkins, and AWS.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published