Skip to content

Latest commit

 

History

History
386 lines (301 loc) · 8.28 KB

File metadata and controls

386 lines (301 loc) · 8.28 KB

Selenium Best Practices

Script Structure

Template

#!/usr/bin/env python3
"""
Script: automation_example.py
Purpose: Automate web task
Author: Your Name
Date: 2024-01-01
"""

import logging
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.chrome.options import Options

# Setup logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

class BrowserAutomation:
    def __init__(self, driver_path="./drivers/chromedriver"):
        """Initialize browser automation."""
        options = Options()
        options.add_argument("--incognito")
        self.driver = webdriver.Chrome(
            executable_path=driver_path,
            options=options
        )
        self.wait = WebDriverWait(self.driver, 10)
        logger.info("Browser initialized")
    
    def navigate(self, url):
        """Navigate to URL."""
        self.driver.get(url)
        logger.info(f"Navigated to {url}")
    
    def find_element(self, by, value):
        """Find element with explicit wait."""
        try:
            element = self.wait.until(
                EC.presence_of_element_located((by, value))
            )
            logger.info(f"Found element: {value}")
            return element
        except Exception as e:
            logger.error(f"Element not found: {e}")
            return None
    
    def click_element(self, by, value):
        """Click element."""
        try:
            element = self.wait.until(
                EC.element_to_be_clickable((by, value))
            )
            element.click()
            logger.info(f"Clicked element: {value}")
        except Exception as e:
            logger.error(f"Click failed: {e}")
    
    def send_keys(self, by, value, text):
        """Send text to element."""
        try:
            element = self.find_element(by, value)
            element.clear()
            element.send_keys(text)
            logger.info(f"Sent keys to {value}: {text}")
        except Exception as e:
            logger.error(f"Send keys failed: {e}")
    
    def close(self):
        """Close browser and cleanup."""
        self.driver.quit()
        logger.info("Browser closed")

def main():
    """Main execution."""
    automation = None
    try:
        automation = BrowserAutomation()
        automation.navigate("https://example.com")
        
        # Your automation logic here
        
    except Exception as e:
        logger.error(f"Error: {e}")
    finally:
        if automation:
            automation.close()

if __name__ == "__main__":
    main()

Exception Handling

Common Exceptions

from selenium.common.exceptions import (
    NoSuchElementException,
    TimeoutException,
    StaleElementReferenceException,
    WebDriverException
)

try:
    element = driver.find_element(By.ID, "element_id")
except NoSuchElementException:
    print("Element not found")
except TimeoutException:
    print("Wait timeout")
except StaleElementReferenceException:
    print("Element reference is stale")
except WebDriverException as e:
    print(f"WebDriver error: {e}")

Waiting Strategies

Avoid Hard-Coded Waits

# ❌ Bad
import time
time.sleep(5)  # Hard-coded delay

# ✅ Good
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

element = WebDriverWait(driver, 10).until(
    EC.presence_of_element_located((By.ID, "element_id"))
)

Wait for Dynamic Content

from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

# Wait for element to be clickable
wait = WebDriverWait(driver, 10)
element = wait.until(
    EC.element_to_be_clickable((By.ID, "button_id"))
)
element.click()

Safe Browser Cleanup

Try/Finally Pattern

driver = None
try:
    driver = webdriver.Chrome("./drivers/chromedriver")
    driver.get("https://example.com")
    # Do stuff
finally:
    if driver:
        driver.quit()

Context Manager Pattern

from contextlib import contextmanager

@contextmanager
def browser():
    driver = webdriver.Chrome("./drivers/chromedriver")
    try:
        yield driver
    finally:
        driver.quit()

with browser() as driver:
    driver.get("https://example.com")
    # Do stuff

Authentication

Handling Login

from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

# Navigate to login page
driver.get("https://example.com/login")

# Fill credentials
email_field = driver.find_element(By.ID, "email")
email_field.send_keys("user@example.com")

password_field = driver.find_element(By.ID, "password")
password_field.send_keys("password123")

# Submit form
login_button = driver.find_element(By.ID, "login_button")
login_button.click()

# Wait for redirect
WebDriverWait(driver, 10).until(
    EC.url_contains("/dashboard")
)

Using Cookies (After Initial Login)

import json

# First time: Login and save cookies
driver.get("https://example.com")
# ... perform login ...

# Save cookies
cookies = driver.get_cookies()
with open("cookies.json", "w") as f:
    json.dump(cookies, f)

# Later: Load cookies to skip login
driver = webdriver.Chrome()
driver.get("https://example.com")
with open("cookies.json", "r") as f:
    cookies = json.load(f)
    for cookie in cookies:
        driver.add_cookie(cookie)

Working with Multiple Windows

# Get window handles
original_window = driver.current_window_handle

# Open new tab
driver.execute_script("window.open('');")
driver.switch_to.window(driver.window_handles[1])
driver.get("https://example.com")

# Switch back to original
driver.switch_to.window(original_window)

# Close all handles
for handle in driver.window_handles:
    driver.switch_to.window(handle)
    driver.close()

Working with Alerts

from selenium.webdriver.common.alert import Alert

# Accept alert
alert = Alert(driver)
alert.accept()

# Dismiss alert
alert.dismiss()

# Get alert text
text = alert.text

# Type in alert
alert.send_keys("text")

Performance Optimization

Headless Mode

options = Options()
options.add_argument("--headless")  # Run without GUI
driver = webdriver.Chrome(options=options)

Disable Images

options = Options()
options.add_argument("--blink-settings=imagesEnabled=false")
driver = webdriver.Chrome(options=options)

Parallel Execution

from concurrent.futures import ThreadPoolExecutor

def run_test(test_data):
    driver = webdriver.Chrome()
    try:
        # Run test
        pass
    finally:
        driver.quit()

with ThreadPoolExecutor(max_workers=4) as executor:
    executor.map(run_test, test_data_list)

Debugging

Screenshot on Failure

def take_screenshot(driver, filename):
    driver.save_screenshot(filename)
    print(f"Screenshot saved: {filename}")

try:
    element = driver.find_element(By.ID, "element_id")
except Exception as e:
    take_screenshot(driver, "error.png")
    raise

Print Page Source

print(driver.page_source)

Get Browser Logs

logs = driver.get_log('browser')
for log in logs:
    print(log)

Testing Best Practices

Use Page Object Model

class LoginPage:
    def __init__(self, driver):
        self.driver = driver
    
    @property
    def email_field(self):
        return self.driver.find_element(By.ID, "email")
    
    @property
    def password_field(self):
        return self.driver.find_element(By.ID, "password")
    
    def login(self, email, password):
        self.email_field.send_keys(email)
        self.password_field.send_keys(password)
        self.driver.find_element(By.ID, "login").click()

# Usage
page = LoginPage(driver)
page.login("user@example.com", "password")

Use Data-Driven Testing

import pytest

@pytest.mark.parametrize("email,password", [
    ("user1@example.com", "pass1"),
    ("user2@example.com", "pass2"),
    ("user3@example.com", "pass3"),
])
def test_login(driver, email, password):
    page = LoginPage(driver)
    page.login(email, password)
    assert driver.current_url == "https://example.com/dashboard"