本文目录导读:

我来详细介绍如何使用 Selenium 进行 Python 端到端测试。
环境准备
安装依赖
pip install selenium pip install webdriver-manager # 自动管理浏览器驱动
基础框架选择(推荐)
pip install pytest pip install pytest-html # HTML测试报告 pip install allure-pytest # Allure报告(可选)
完整的端到端测试示例
示例:测试登录功能
import pytest
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.service import Service
from webdriver_manager.chrome import ChromeDriverManager
import time
class TestLoginE2E:
"""端到端测试:登录功能"""
@pytest.fixture(scope="class")
def setup(self):
"""测试夹具:初始化浏览器"""
# 设置Chrome选项
chrome_options = webdriver.ChromeOptions()
chrome_options.add_argument('--headless') # 无头模式
chrome_options.add_argument('--no-sandbox')
chrome_options.add_argument('--disable-dev-shm-usage')
chrome_options.add_argument('--window-size=1920,1080')
# 初始化驱动
service = Service(ChromeDriverManager().install())
driver = webdriver.Chrome(service=service, options=chrome_options)
driver.implicitly_wait(10) # 隐式等待
yield driver
# 测试结束后清理
driver.quit()
def test_successful_login(self, setup):
"""测试成功登录"""
driver = setup
try:
# 1. 打开登录页面
driver.get("https://example.com/login")
# 2. 输入用户名
username_input = WebDriverWait(driver, 10).until(
EC.presence_of_element_located((By.ID, "username"))
)
username_input.send_keys("test_user@example.com")
# 3. 输入密码
password_input = driver.find_element(By.ID, "password")
password_input.send_keys("TestPassword123!")
# 4. 点击登录按钮
login_button = driver.find_element(By.XPATH, "//button[@type='submit']")
login_button.click()
# 5. 验证登录成功 - 检查是否跳转到首页
WebDriverWait(driver, 10).until(
EC.url_contains("dashboard")
)
# 6. 验证用户信息
user_menu = driver.find_element(By.CLASS_NAME, "user-menu")
assert user_menu.is_displayed(), "用户菜单未显示"
# 7. 获取页面标题
assert "Dashboard" in driver.title
print("✓ 登录测试通过")
except Exception as e:
# 截图保存失败现场
driver.save_screenshot("login_failure.png")
raise e
def test_invalid_login(self, setup):
"""测试无效登录"""
driver = setup
# 1. 打开登录页面
driver.get("https://example.com/login")
# 2. 输入错误的凭据
username_input = WebDriverWait(driver, 10).until(
EC.presence_of_element_located((By.ID, "username"))
)
username_input.send_keys("wrong@email.com")
password_input = driver.find_element(By.ID, "password")
password_input.send_keys("WrongPassword")
# 3. 点击登录
login_button = driver.find_element(By.XPATH, "//button[@type='submit']")
login_button.click()
# 4. 验证错误消息
error_message = WebDriverWait(driver, 10).until(
EC.visibility_of_element_located((By.CLASS_NAME, "error-message"))
)
assert error_message.is_displayed(), "错误消息未显示"
assert "Invalid credentials" in error_message.text
# 5. 确认仍在登录页面
assert "login" in driver.current_url.lower()
print("✓ 无效登录测试通过")
class TestUserFlowE2E:
"""端到端测试:用户完整流程"""
@pytest.fixture(scope="class")
def authenticated_session(self):
"""预登录会话"""
chrome_options = webdriver.ChromeOptions()
driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()),
options=chrome_options)
driver.implicitly_wait(10)
# 执行登录
driver.get("https://example.com/login")
driver.find_element(By.ID, "username").send_keys("test@example.com")
driver.find_element(By.ID, "password").send_keys("Test123!")
driver.find_element(By.XPATH, "//button[@type='submit']").click()
# 等待登录完成
WebDriverWait(driver, 10).until(
EC.presence_of_element_located((By.CLASS_NAME, "user-menu"))
)
yield driver
driver.quit()
def test_complete_order_flow(self, authenticated_session):
"""测试完整订单流程"""
driver = authenticated_session
# 1. 搜索商品
search_input = driver.find_element(By.NAME, "search")
search_input.send_keys("laptop\n")
# 2. 选择第一个商品
first_product = WebDriverWait(driver, 10).until(
EC.element_to_be_clickable((By.CSS_SELECTOR, ".product-card:first-child a"))
)
first_product.click()
# 3. 添加到购物车
add_to_cart = WebDriverWait(driver, 10).until(
EC.element_to_be_clickable((By.ID, "add-to-cart"))
)
add_to_cart.click()
# 4. 验证购物车更新
cart_count = driver.find_element(By.CLASS_NAME, "cart-count")
assert cart_count.text == "1", "购物车数量不正确"
# 5. 进入购物车
driver.find_element(By.CLASS_NAME, "cart-icon").click()
# 6. 结算
checkout_button = WebDriverWait(driver, 10).until(
EC.element_to_be_clickable((By.ID, "checkout"))
)
checkout_button.click()
# 7. 填写订单信息
driver.find_element(By.ID, "address").send_keys("123 Test St")
driver.find_element(By.ID, "city").send_keys("Test City")
driver.find_element(By.ID, "zip").send_keys("12345")
# 8. 提交订单
driver.find_element(By.ID, "submit-order").click()
# 9. 验证订单确认
confirmation = WebDriverWait(driver, 10).until(
EC.visibility_of_element_located((By.CLASS_NAME, "order-confirmation"))
)
assert confirmation.is_displayed(), "订单确认页面未显示"
print("✓ 订单流程测试通过")
def test_api_response_verification(self, authenticated_session):
"""验证API响应(通过浏览器)"""
driver = authenticated_session
# 监控网络请求
logs = driver.execute_script("""
var performance = window.performance ||
window.webkitPerformance ||
window.mozPerformance ||
window.msPerformance;
var entries = performance.getEntriesByType('resource');
var apiCalls = entries.filter(entry =>
entry.name.includes('/api/')
);
return apiCalls.map(call => ({
url: call.name,
duration: call.duration,
size: call.transferSize
}));
""")
# 验证API调用
assert len(logs) > 0, "未检测到API调用"
for api_call in logs:
print(f"API: {api_call['url']} - Duration: {api_call['duration']}ms")
assert api_call['duration'] < 3000, f"API响应时间过长: {api_call['url']}"
class TestResponsiveDesign:
"""响应式设计测试"""
@pytest.mark.parametrize("width,height", [
(375, 667), # iPhone 6/7/8
(414, 896), # iPhone 11 Pro Max
(768, 1024), # iPad
(1920, 1080) # Desktop
])
def test_responsive_layout(self, width, height):
"""测试不同屏幕尺寸下的布局"""
chrome_options = webdriver.ChromeOptions()
driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()),
options=chrome_options)
try:
# 设置窗口大小
driver.set_window_size(width, height)
# 加载页面
driver.get("https://example.com")
# 检查响应式元素
if width < 768: # 移动端
hamburger_menu = driver.find_element(By.CLASS_NAME, "mobile-menu-toggle")
assert hamburger_menu.is_displayed(), f"移动端菜单在 {width}x{height} 下未显示"
else: # 桌面端
nav_menu = driver.find_element(By.CLASS_NAME, "desktop-nav")
assert nav_menu.is_displayed(), f"桌面导航在 {width}x{height} 下未显示"
print(f"✓ 响应式测试通过: {width}x{height}")
finally:
driver.quit()
# 运行测试
if __name__ == "__main__":
pytest.main(["-v", "--html=report.html", __file__])
实用的辅助函数
# utils/selenium_utils.py
import time
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
class SeleniumUtils:
"""Selenium 辅助工具类"""
@staticmethod
def wait_and_click(driver, locator, timeout=10):
"""等待元素可点击并点击"""
element = WebDriverWait(driver, timeout).until(
EC.element_to_be_clickable(locator)
)
element.click()
return element
@staticmethod
def wait_and_send_keys(driver, locator, text, timeout=10):
"""等待输入框并输入文本"""
element = WebDriverWait(driver, timeout).until(
EC.presence_of_element_located(locator)
)
element.clear()
element.send_keys(text)
return element
@staticmethod
def scroll_to_element(driver, element):
"""滚动到元素位置"""
driver.execute_script("arguments[0].scrollIntoView(true);", element)
time.sleep(0.5)
@staticmethod
def highlight_element(driver, element):
"""高亮元素(用于调试)"""
original_style = element.get_attribute("style")
driver.execute_script(
"arguments[0].setAttribute('style', 'background: yellow; border: 2px solid red;');",
element
)
time.sleep(0.5)
driver.execute_script(
f"arguments[0].setAttribute('style', '{original_style}');",
element
)
@staticmethod
def get_page_load_time(driver):
"""获取页面加载时间"""
return driver.execute_script("""
return window.performance.timing.loadEventEnd -
window.performance.timing.navigationStart;
""")
# conftest.py - pytest配置文件
import pytest
from selenium import webdriver
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.chrome.service import Service
@pytest.fixture(scope="session")
def browser():
"""全局浏览器实例"""
chrome_options = webdriver.ChromeOptions()
chrome_options.add_argument('--headless')
chrome_options.add_argument('--no-sandbox')
chrome_options.add_argument('--disable-dev-shm-usage')
driver = webdriver.Chrome(
service=Service(ChromeDriverManager().install()),
options=chrome_options
)
driver.implicitly_wait(10)
driver.maximize_window()
yield driver
driver.quit()
@pytest.hookimpl(tryfirst=True, hookwrapper=True)
def pytest_runtest_makereport(item, call):
"""测试失败时截图"""
outcome = yield
report = outcome.get_result()
if report.when == "call" and report.failed:
if "browser" in item.fixturenames:
driver = item.funcargs["browser"]
screenshot_name = f"screenshots/{item.name}_{int(time.time())}.png"
driver.save_screenshot(screenshot_name)
print(f"截图保存: {screenshot_name}")
运行测试
# 运行所有测试 pytest test_e2e.py -v # 运行特定测试类 pytest test_e2e.py::TestLoginE2E -v # 生成HTML报告 pytest test_e2e.py -v --html=report.html # 并行执行测试 pytest test_e2e.py -v -n 4 # 失败重试 pytest test_e2e.py -v --reruns 2
CI/CD 集成示例
# .github/workflows/e2e-tests.yml
name: E2E Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: '3.9'
- name: Install dependencies
run: |
pip install selenium pytest pytest-html webdriver-manager
- name: Run E2E tests
run: |
pytest test_e2e.py -v --html=report.html
- name: Upload test report
uses: actions/upload-artifact@v2
if: always()
with:
name: test-report
path: report.html
- name: Upload screenshots
uses: actions/upload-artifact@v2
if: failure()
with:
name: screenshots
path: screenshots/
关键最佳实践
- 使用显式等待:避免使用
time.sleep() - 页面对象模式:封装页面元素和操作
- 失败截图:测试失败时自动截图
- 报告生成:生成详细的测试报告
- 并行执行:提高测试效率
- 数据驱动:使用参数化测试
这个框架可以满足大部分端到端测试需求,你可以根据具体项目进行调整。
版权声明:足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播