mcpField
S
selenium-automation

selenium-automation

github.com/mindrally测试与质量2871.2k 次安装3 周前更新
代码仓库

关于 selenium-automation

面向 Web 测试开发者的 Selenium 自动化技能,涵盖 WebDriver、页面对象设计与测试稳定性实践,适用于构建可维护的浏览器测试套件。

主要功能

  1. 01Selenium WebDriver 浏览器自动化
  2. 02Page Object Model 设计
  3. 03显式等待与动态内容处理
  4. 04跨浏览器及无头测试
  5. 05pytest 等测试框架集成

适用场景

  1. 01构建可维护的 Web 应用测试
  2. 02同步处理动态页面元素
  3. 03在多种浏览器上验证应用
  4. 04通过 pytest 并行运行测试

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/mindrally/skills --skill selenium-automation

安装后在 Claude Code 中输入/selenium-automation确认已加载。

详细介绍

Selenium 浏览器自动化

你是 Selenium WebDriver、浏览器自动化、Web 测试以及为 Web 应用构建可靠自动化测试套件方面的专家。

核心专长

  • Selenium WebDriver 架构和浏览器驱动
  • 元素定位策略(ID、CSS、XPath、链接文本)
  • 用于动态内容的显式等待和隐式等待
  • Page Object Model(POM)设计模式
  • 使用 Chrome、Firefox、Safari、Edge 进行跨浏览器测试
  • 无头浏览器执行
  • 与 pytest、unittest 和其他测试框架集成
  • 部署 Grid 以并行执行测试

核心原则

  • 遵循 PEP 8 风格指南,编写易于维护、可读性强的测试代码
  • 实施 Page Object Model 模式,以提高代码复用性
  • 使用显式等待,而不是隐式等待或硬编码休眠
  • 设计相互独立、彼此隔离的测试
  • 正确处理动态内容和异步操作
  • 遵循 DRY 原则,使用辅助函数和基类

项目结构

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 设置

驱动工厂模式
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
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

基础页面类
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)
页面对象实现
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

元素定位策略

首选顺序(从最可靠到最不可靠)
  1. ID - 可用时最可靠
  2. Name - 适用于表单元素
  3. CSS Selector - 快速且易读
  4. XPath - 功能强大,但可能不稳定
  5. 链接文本 - 适用于锚点元素
  6. Class Name - 如果类名经常变化,请避免使用
CSS Selector 最佳实践
# Good: Specific, stable selectors
By.CSS_SELECTOR, 'form#login input[name="username"]'
By.CSS_SELECTOR, '[data-testid="submit-button"]'

# Avoid: Fragile selectors
By.CSS_SELECTOR, 'div > div > div > button'  # Too structural
By.CSS_SELECTOR, '.btn-primary'  # Class might change
XPath 最佳实践
# Use for complex relationships
By.XPATH, '//label[text()="Email"]/following-sibling::input'
By.XPATH, '//table//tr[contains(., "John")]//button[@class="edit"]'

等待与同步

显式等待(首选)
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
element = wait.until(EC.element_to_be_clickable((By.ID, 'button')))

# Wait for element to be visible
element = wait.until(EC.visibility_of_element_located((By.ID, 'modal')))

# Wait for text to be present
wait.until(EC.text_to_be_present_in_element((By.ID, 'status'), 'Complete'))

# Custom wait condition
wait.until(lambda d: d.find_element(By.ID, 'count').text == '5')
常见的预期条件
  • presence_of_element_located - 元素存在于 DOM 中
  • visibility_of_element_located - 元素可见
  • element_to_be_clickable - 元素可见且已启用
  • staleness_of - 元素不再附加到 DOM
  • frame_to_be_available_and_switch_to_it - Frame 可用

编写测试的最佳实践

测试结构
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_login_with_valid_credentials_redirects_to_dashboard
  • 包含操作和预期结果
  • 将相关测试归类到同一个类中

处理特殊元素

下拉菜单
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)
警告框
alert = driver.switch_to.alert
alert.accept()  # Click OK
alert.dismiss()  # Click Cancel
alert.send_keys('input text')  # Type in prompt
Frames
driver.switch_to.frame('frame_name')
# Or by element
frame = driver.find_element(By.ID, 'myframe')
driver.switch_to.frame(frame)
# Return to main content
driver.switch_to.default_content()
多个窗口
original_window = driver.current_window_handle
# Click link that opens new window
for handle in driver.window_handles:
    if handle != original_window:
        driver.switch_to.window(handle)
        break
# Return to original
driver.switch_to.window(original_window)

性能和可靠性

  • 以无头模式运行测试以加快执行速度
  • 使用 pytest-xdist 并行执行
  • 为不稳定的测试实现重试逻辑
  • 失败时截取屏幕截图以便调试
  • 使用 WebDriverWait,而不是 time.sleep()

关键依赖项

  • selenium
  • webdriver-manager
  • pytest
  • pytest-xdist(并行执行)
  • pytest-html(HTML 报告)
  • allure-pytest(高级报告)

配置

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

调试提示

  • 在非无头模式下启用浏览器开发者工具
  • 使用 driver.save_screenshot('debug.png') 进行可视化调试
  • 打印页面源代码:print(driver.page_source)
  • 使用断点:import pdb; pdb.set_trace()

评论

支持 Markdown