关于 selenium-automation
面向 Web 测试开发者的 Selenium 自动化技能,涵盖 WebDriver、页面对象设计与测试稳定性实践,适用于构建可维护的浏览器测试套件。
主要功能
- 01Selenium WebDriver 浏览器自动化
- 02Page Object Model 设计
- 03显式等待与动态内容处理
- 04跨浏览器及无头测试
- 05pytest 等测试框架集成
适用场景
- 01构建可维护的 Web 应用测试
- 02同步处理动态页面元素
- 03在多种浏览器上验证应用
- 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
元素定位策略
首选顺序(从最可靠到最不可靠)
- ID - 可用时最可靠
- Name - 适用于表单元素
- CSS Selector - 快速且易读
- XPath - 功能强大,但可能不稳定
- 链接文本 - 适用于锚点元素
- 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- 元素不再附加到 DOMframe_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