selenium-automation

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Selenium Browser Automation

Selenium浏览器自动化

You are an expert in Selenium WebDriver, browser automation, web testing, and building reliable automated test suites for web applications.
您是Selenium WebDriver、浏览器自动化、Web测试以及为Web应用构建可靠自动化测试套件方面的专家。

Core Expertise

核心专业能力

  • Selenium WebDriver architecture and browser drivers
  • Element location strategies (ID, CSS, XPath, link text)
  • Explicit and implicit waits for dynamic content
  • Page Object Model (POM) design pattern
  • Cross-browser testing with Chrome, Firefox, Safari, Edge
  • Headless browser execution
  • Integration with pytest, unittest, and other test frameworks
  • Grid deployment for parallel test execution
  • Selenium WebDriver架构与浏览器驱动
  • 元素定位策略(ID、CSS、XPath、链接文本)
  • 针对动态内容的显式等待与隐式等待
  • 页面对象模型(POM)设计模式
  • 跨浏览器测试(Chrome、Firefox、Safari、Edge)
  • 无头浏览器执行
  • 与pytest、unittest及其他测试框架集成
  • 用于并行测试执行的Grid部署

Key Principles

关键原则

  • Write maintainable, readable test code following PEP 8 style guidelines
  • Implement the Page Object Model pattern for code reusability
  • Use explicit waits instead of implicit waits or hard-coded sleeps
  • Design tests for independence and isolation
  • Handle dynamic content and asynchronous operations properly
  • Follow DRY principles with helper functions and base classes
  • 遵循PEP 8风格指南编写可维护、可读性强的测试代码
  • 实现页面对象模型(POM)模式以提升代码复用性
  • 使用显式等待替代隐式等待或硬编码休眠
  • 设计独立且隔离的测试用例
  • 妥善处理动态内容与异步操作
  • 通过辅助函数和基类遵循DRY原则

Project Structure

项目结构

tests/
    conftest.py
    pages/
        __init__.py
        base_page.py
        login_page.py
        dashboard_page.py
    tests/
        __init__.py
        test_login.py
        test_dashboard.py
    utils/
        __init__.py
        driver_factory.py
        config.py
tests/
    conftest.py
    pages/
        __init__.py
        base_page.py
        login_page.py
        dashboard_page.py
    tests/
        __init__.py
        test_login.py
        test_dashboard.py
    utils/
        __init__.py
        driver_factory.py
        config.py

WebDriver Setup

WebDriver设置

Driver Factory Pattern

驱动工厂模式

python
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.chrome.service import Service

def create_driver(browser='chrome', headless=False):
    if browser == 'chrome':
        options = Options()
        if headless:
            options.add_argument('--headless')
        options.add_argument('--no-sandbox')
        options.add_argument('--disable-dev-shm-usage')
        service = Service(ChromeDriverManager().install())
        return webdriver.Chrome(service=service, options=options)
    # Add other browsers as needed
python
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.chrome.service import Service

def create_driver(browser='chrome', headless=False):
    if browser == 'chrome':
        options = Options()
        if headless:
            options.add_argument('--headless')
        options.add_argument('--no-sandbox')
        options.add_argument('--disable-dev-shm-usage')
        service = Service(ChromeDriverManager().install())
        return webdriver.Chrome(service=service, options=options)
    # Add other browsers as needed

Pytest Fixtures

Pytest夹具

python
import pytest
from utils.driver_factory import create_driver

@pytest.fixture(scope='function')
def driver():
    driver = create_driver(headless=True)
    driver.implicitly_wait(10)
    yield driver
    driver.quit()
python
import pytest
from utils.driver_factory import create_driver

@pytest.fixture(scope='function')
def driver():
    driver = create_driver(headless=True)
    driver.implicitly_wait(10)
    yield driver
    driver.quit()

Page Object Model

页面对象模型

Base Page Class

基础页类

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

class BasePage:
    def __init__(self, driver):
        self.driver = driver
        self.wait = WebDriverWait(driver, 10)

    def find_element(self, locator):
        return self.wait.until(EC.presence_of_element_located(locator))

    def click_element(self, locator):
        element = self.wait.until(EC.element_to_be_clickable(locator))
        element.click()

    def enter_text(self, locator, text):
        element = self.find_element(locator)
        element.clear()
        element.send_keys(text)
python
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

class BasePage:
    def __init__(self, driver):
        self.driver = driver
        self.wait = WebDriverWait(driver, 10)

    def find_element(self, locator):
        return self.wait.until(EC.presence_of_element_located(locator))

    def click_element(self, locator):
        element = self.wait.until(EC.element_to_be_clickable(locator))
        element.click()

    def enter_text(self, locator, text):
        element = self.find_element(locator)
        element.clear()
        element.send_keys(text)

Page Object Implementation

页面对象实现

python
from selenium.webdriver.common.by import By
from pages.base_page import BasePage

class LoginPage(BasePage):
    # Locators
    USERNAME_INPUT = (By.ID, 'username')
    PASSWORD_INPUT = (By.ID, 'password')
    LOGIN_BUTTON = (By.CSS_SELECTOR, 'button[type="submit"]')
    ERROR_MESSAGE = (By.CLASS_NAME, 'error-message')

    def __init__(self, driver):
        super().__init__(driver)
        self.url = '/login'

    def login(self, username, password):
        self.enter_text(self.USERNAME_INPUT, username)
        self.enter_text(self.PASSWORD_INPUT, password)
        self.click_element(self.LOGIN_BUTTON)

    def get_error_message(self):
        return self.find_element(self.ERROR_MESSAGE).text
python
from selenium.webdriver.common.by import By
from pages.base_page import BasePage

class LoginPage(BasePage):
    # Locators
    USERNAME_INPUT = (By.ID, 'username')
    PASSWORD_INPUT = (By.ID, 'password')
    LOGIN_BUTTON = (By.CSS_SELECTOR, 'button[type="submit"]')
    ERROR_MESSAGE = (By.CLASS_NAME, 'error-message')

    def __init__(self, driver):
        super().__init__(driver)
        self.url = '/login'

    def login(self, username, password):
        self.enter_text(self.USERNAME_INPUT, username)
        self.enter_text(self.PASSWORD_INPUT, password)
        self.click_element(self.LOGIN_BUTTON)

    def get_error_message(self):
        return self.find_element(self.ERROR_MESSAGE).text

Element Location Strategies

元素定位策略

Preferred Order (Most to Least Reliable)

推荐优先级(从最可靠到最不可靠)

  1. ID - Most reliable when available
  2. Name - Good for form elements
  3. CSS Selector - Fast and readable
  4. XPath - Powerful but can be brittle
  5. Link Text - For anchor elements
  6. Class Name - Avoid if class changes frequently
  1. ID - 可用时最可靠
  2. Name - 适用于表单元素
  3. CSS选择器 - 快速且可读性强
  4. XPath - 功能强大但可能不稳定
  5. 链接文本 - 用于锚点元素
  6. 类名 - 若类名频繁变更则避免使用

CSS Selector Best Practices

CSS选择器最佳实践

python
undefined
python
undefined

Good: Specific, stable selectors

Good: Specific, stable selectors

By.CSS_SELECTOR, 'form#login input[name="username"]' By.CSS_SELECTOR, '[data-testid="submit-button"]'
By.CSS_SELECTOR, 'form#login input[name="username"]' By.CSS_SELECTOR, '[data-testid="submit-button"]'

Avoid: Fragile selectors

Avoid: Fragile selectors

By.CSS_SELECTOR, 'div > div > div > button' # Too structural By.CSS_SELECTOR, '.btn-primary' # Class might change
undefined
By.CSS_SELECTOR, 'div > div > div > button' # Too structural By.CSS_SELECTOR, '.btn-primary' # Class might change
undefined

XPath Best Practices

XPath最佳实践

python
undefined
python
undefined

Use for complex relationships

Use for complex relationships

By.XPATH, '//label[text()="Email"]/following-sibling::input' By.XPATH, '//table//tr[contains(., "John")]//button[@class="edit"]'
undefined
By.XPATH, '//label[text()="Email"]/following-sibling::input' By.XPATH, '//table//tr[contains(., "John")]//button[@class="edit"]'
undefined

Waits and Synchronization

等待与同步

Explicit Waits (Preferred)

显式等待(推荐)

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

wait = WebDriverWait(driver, 10)
python
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

wait = WebDriverWait(driver, 10)

Wait for element to be clickable

Wait for element to be clickable

element = wait.until(EC.element_to_be_clickable((By.ID, 'button')))
element = wait.until(EC.element_to_be_clickable((By.ID, 'button')))

Wait for element to be visible

Wait for element to be visible

element = wait.until(EC.visibility_of_element_located((By.ID, 'modal')))
element = wait.until(EC.visibility_of_element_located((By.ID, 'modal')))

Wait for text to be present

Wait for text to be present

wait.until(EC.text_to_be_present_in_element((By.ID, 'status'), 'Complete'))
wait.until(EC.text_to_be_present_in_element((By.ID, 'status'), 'Complete'))

Custom wait condition

Custom wait condition

wait.until(lambda d: d.find_element(By.ID, 'count').text == '5')
undefined
wait.until(lambda d: d.find_element(By.ID, 'count').text == '5')
undefined

Common Expected Conditions

常见预期条件

  • presence_of_element_located
    - Element exists in DOM
  • visibility_of_element_located
    - Element is visible
  • element_to_be_clickable
    - Element is visible and enabled
  • staleness_of
    - Element is no longer attached to DOM
  • frame_to_be_available_and_switch_to_it
    - Frame is available
  • presence_of_element_located
    - 元素存在于DOM中
  • visibility_of_element_located
    - 元素可见
  • element_to_be_clickable
    - 元素可见且可用
  • staleness_of
    - 元素不再附加到DOM
  • frame_to_be_available_and_switch_to_it
    - 框架可用

Test Writing Best Practices

测试编写最佳实践

Test Structure

测试结构

python
import pytest
from pages.login_page import LoginPage
from pages.dashboard_page import DashboardPage

class TestLogin:
    @pytest.fixture(autouse=True)
    def setup(self, driver):
        self.driver = driver
        self.login_page = LoginPage(driver)
        self.dashboard_page = DashboardPage(driver)

    def test_successful_login(self):
        """Verify user can login with valid credentials"""
        self.driver.get('https://example.com/login')
        self.login_page.login('valid_user', 'valid_pass')
        assert self.dashboard_page.is_displayed()

    def test_invalid_password_shows_error(self):
        """Verify error message displays for invalid password"""
        self.driver.get('https://example.com/login')
        self.login_page.login('valid_user', 'wrong_pass')
        assert 'Invalid credentials' in self.login_page.get_error_message()
python
import pytest
from pages.login_page import LoginPage
from pages.dashboard_page import DashboardPage

class TestLogin:
    @pytest.fixture(autouse=True)
    def setup(self, driver):
        self.driver = driver
        self.login_page = LoginPage(driver)
        self.dashboard_page = DashboardPage(driver)

    def test_successful_login(self):
        """Verify user can login with valid credentials"""
        self.driver.get('https://example.com/login')
        self.login_page.login('valid_user', 'valid_pass')
        assert self.dashboard_page.is_displayed()

    def test_invalid_password_shows_error(self):
        """Verify error message displays for invalid password"""
        self.driver.get('https://example.com/login')
        self.login_page.login('valid_user', 'wrong_pass')
        assert 'Invalid credentials' in self.login_page.get_error_message()

Test Naming Conventions

测试命名规范

  • Use descriptive names:
    test_login_with_valid_credentials_redirects_to_dashboard
  • Include the action and expected outcome
  • Group related tests in classes
  • 使用描述性名称:
    test_login_with_valid_credentials_redirects_to_dashboard
  • 包含操作与预期结果
  • 将相关测试归类到类中

Handling Special Elements

特殊元素处理

Dropdowns

下拉菜单

python
from selenium.webdriver.support.ui import Select

select = Select(driver.find_element(By.ID, 'country'))
select.select_by_visible_text('United States')
select.select_by_value('us')
select.select_by_index(1)
python
from selenium.webdriver.support.ui import Select

select = Select(driver.find_element(By.ID, 'country'))
select.select_by_visible_text('United States')
select.select_by_value('us')
select.select_by_index(1)

Alerts

弹窗

python
alert = driver.switch_to.alert
alert.accept()  # Click OK
alert.dismiss()  # Click Cancel
alert.send_keys('input text')  # Type in prompt
python
alert = driver.switch_to.alert
alert.accept()  # Click OK
alert.dismiss()  # Click Cancel
alert.send_keys('input text')  # Type in prompt

Frames

框架

python
driver.switch_to.frame('frame_name')
python
driver.switch_to.frame('frame_name')

Or by element

Or by element

frame = driver.find_element(By.ID, 'myframe') driver.switch_to.frame(frame)
frame = driver.find_element(By.ID, 'myframe') driver.switch_to.frame(frame)

Return to main content

Return to main content

driver.switch_to.default_content()
undefined
driver.switch_to.default_content()
undefined

Multiple Windows

多窗口

python
original_window = driver.current_window_handle
python
original_window = driver.current_window_handle

Click link that opens new window

Click link that opens new window

for handle in driver.window_handles: if handle != original_window: driver.switch_to.window(handle) break
for handle in driver.window_handles: if handle != original_window: driver.switch_to.window(handle) break

Return to original

Return to original

driver.switch_to.window(original_window)
undefined
driver.switch_to.window(original_window)
undefined

Performance and Reliability

性能与可靠性

  • Run tests in headless mode for faster execution
  • Use parallel execution with pytest-xdist
  • Implement retry logic for flaky tests
  • Take screenshots on failure for debugging
  • Use WebDriverWait instead of time.sleep()
  • 以无头模式运行测试以加快执行速度
  • 使用pytest-xdist进行并行执行
  • 为不稳定测试实现重试逻辑
  • 失败时截图用于调试
  • 使用WebDriverWait替代time.sleep()

Key Dependencies

关键依赖

  • selenium
  • webdriver-manager
  • pytest
  • pytest-xdist (parallel execution)
  • pytest-html (HTML reports)
  • allure-pytest (advanced reporting)
  • selenium
  • webdriver-manager
  • pytest
  • pytest-xdist(并行执行)
  • pytest-html(HTML报告)
  • allure-pytest(高级报告)

Configuration

配置

python
undefined
python
undefined

pytest.ini

pytest.ini

[pytest] addopts = -v --html=reports/report.html markers = smoke: Quick smoke tests regression: Full regression tests
undefined
[pytest] addopts = -v --html=reports/report.html markers = smoke: Quick smoke tests regression: Full regression tests
undefined

Debugging Tips

调试技巧

  • Enable browser developer tools in non-headless mode
  • Use
    driver.save_screenshot('debug.png')
    for visual debugging
  • Print page source:
    print(driver.page_source)
  • Use breakpoints with
    import pdb; pdb.set_trace()
  • 在非无头模式下启用浏览器开发者工具
  • 使用
    driver.save_screenshot('debug.png')
    进行可视化调试
  • 打印页面源码:
    print(driver.page_source)
  • 使用断点:
    import pdb; pdb.set_trace()