Python脚本端到端测试Selenium如何用

wen 实用脚本 2

本文目录导读:

Python脚本端到端测试Selenium如何用

  1. 环境准备
  2. 完整的端到端测试示例
  3. 实用的辅助函数
  4. 运行测试
  5. CI/CD 集成示例
  6. 关键最佳实践

我来详细介绍如何使用 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/

关键最佳实践

  1. 使用显式等待:避免使用 time.sleep()
  2. 页面对象模式:封装页面元素和操作
  3. 失败截图:测试失败时自动截图
  4. 报告生成:生成详细的测试报告
  5. 并行执行:提高测试效率
  6. 数据驱动:使用参数化测试

这个框架可以满足大部分端到端测试需求,你可以根据具体项目进行调整。

抱歉,评论功能暂时关闭!