You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Selenium+Pytest项目搭建疑问:Driver配置与类结构优化

问题分析

当前方案存在两个核心问题:

  • BaseClass中的driver是类级属性,所有继承它的测试类、页面对象类会共享同一个driver实例,测试用例之间会互相干扰(比如A测试跳转了页面,B测试启动时driver还停留在该页面)
  • 页面对象被迫使用@classmethod,违背了页面对象模型(POM)的设计初衷——页面对象应该与特定driver实例绑定,是实例化的类,这样才更灵活、易维护
优化方案:用Pytest Fixture管理Driver + 实例化页面对象

这是Selenium+Pytest项目的标准实践,既解决了driver复用/隔离问题,又让页面对象回归合理设计。

1. 定义Driver Fixture

在conftest.py中编写driver fixture,根据需求设置作用域(比如function级每个测试用例启动新driver,class级每个测试类共用一个driver):

# conftest.py
import pytest
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.chrome.options import Options

@pytest.fixture(scope="class")  # 可根据需求改为function/module/session
def driver():
    service_obj = Service()
    chrome_options = Options()
    # 可在此添加全局配置,比如无头模式、窗口大小
    # chrome_options.add_argument("--headless=new")
    driver = webdriver.Chrome(service=service_obj, options=chrome_options)
    driver.maximize_window()
    driver.implicitly_wait(10)  # 全局隐式等待
    yield driver
    # 测试结束后自动清理
    driver.quit()

2. 重构页面对象(去掉继承,改为实例类)

页面对象不再继承BaseClass,而是通过构造函数接收driver实例,方法也无需@classmethod:

# pages/login_page.py
from selenium.webdriver.common.by import By

class LoginPage:
    def __init__(self, driver):
        self.driver = driver
        self.username_input = (By.ID, 'username-input')
        self.password_input = (By.ID, 'username-password')  # 修正原代码变量名错误

    def type_login_credentials(self, login, password):
        self.driver.find_element(*self.username_input).send_keys(login)
        self.driver.find_element(*self.password_input).send_keys(password)
    
    def click_login_button(self):
        self.driver.find_element(By.ID, 'login-button').click()

3. 测试类使用Fixture和实例化页面对象

测试类通过pytest依赖注入获取driver,然后实例化页面对象,无需继承任何基类:

# tests/test_login.py
from pages.login_page import LoginPage
from config import BASE_URL  # 可抽离全局配置到config.py

class TestLogin:
    def test_valid_login(self, driver):
        driver.get(f"{BASE_URL}/login")
        login_page = LoginPage(driver)
        login_page.type_login_credentials("valid_user", "valid_pass")
        login_page.click_login_button()
        # 后续断言逻辑,比如检查跳转后的页面标题

    def test_invalid_login(self, driver):
        driver.get(f"{BASE_URL}/login")
        login_page = LoginPage(driver)
        login_page.type_login_credentials("invalid_user", "invalid_pass")
        login_page.click_login_button()
        # 断言错误提示是否出现

4. 额外优化:全局配置抽离

把浏览器类型、环境URL、超时时间等全局配置单独放在config.py,避免硬编码:

# config.py
BASE_URL = "https://your-app-url.com"
BROWSER = "chrome"
IMPLICIT_WAIT = 10
方案优势
  • 隔离性强:fixture的作用域可精确控制driver生命周期,避免测试用例间的污染
  • 灵活性高:页面对象是实例类,可根据需求创建多个实例,扩展更方便
  • 符合POM设计:页面对象与driver实例绑定,每个页面操作都基于当前浏览器会话,逻辑清晰
  • 无需重复初始化:fixture自动管理driver的创建与销毁,不用在每个测试类重复写初始化代码

内容的提问来源于stack exchange,提问作者Biedrzu

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 14:02:24