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
相关产品推荐
相关产品推荐

