拆分pytest测试代码后,测试文件无法识别初始化后的webdriver实例
问题排查:拆分pytest setup/teardown后driver为None的错误
问题背景
原本单文件的pytest测试代码可正常运行,将setup_function和teardown_function拆分到同包下的pre_test.py后,测试文件执行时抛出错误:
E AttributeError: 'NoneType' object has no attribute 'get'
提示driver仍为None,未被初始化。
原因分析
- pytest钩子函数的作用范围限制:
setup_function和teardown_function是pytest的模块级钩子,只有当它们与测试函数处于同一模块,或通过conftest.py全局共享时,pytest才会自动识别并执行。拆分到pre_test.py后,测试模块无法触发这些钩子,导致driver从未被初始化,始终是初始的None。 - Python导入的赋值特性:即使钩子函数被执行,测试模块中
from .pre_test import driver导入的是导入时刻的None值。当pre_test.py中通过global driver重新赋值时,测试模块里的driver变量不会同步更新,依然保持原有的None引用。
解决方案
方案一:使用pytest Fixture(推荐)
改用pytest的fixture机制替代全局变量和setup/teardown函数,fixture支持跨模块共享,更符合pytest最佳实践:
- 将
pre_test.py重命名为conftest.py,内容修改为:
from selenium import webdriver import pytest @pytest.fixture(scope="function") def driver(): print("Setup") options = webdriver.ChromeOptions() options.add_experimental_option('excludeSwitches', ['enable-logging']) driver_instance = webdriver.Chrome(options) driver_instance.maximize_window() yield driver_instance print("Teardown") driver_instance.quit()
- 测试文件修改为:
import time def test_me1(driver): driver.get("https://www.selenium.dev/") print("Inside t1") time.sleep(3) def test_me2(driver): driver.get("https://www.selenium.dev/") print("Inside t2") time.sleep(3)
- fixture会自动在每个测试函数执行前初始化driver,执行后销毁,无需手动管理全局变量。
- 测试函数通过参数接收driver实例,避免了导入和全局变量的问题。
方案二:强制触发setup/teardown钩子(不推荐)
如果必须保留setup_function的写法,需确保钩子函数被测试模块识别:
测试文件修改为:
from .pre_test import setup_function, teardown_function, driver import time def test_me1(): global driver driver.get("https://www.selenium.dev/") print("Inside t1") time.sleep(3) def test_me2(): global driver driver.get("https://www.selenium.dev/") print("Inside t2") time.sleep(3)
- 导入
setup_function和teardown_function,让pytest在当前模块中识别并执行这些钩子。 - 声明
global driver,确保测试函数使用的是pre_test.py中更新后的driver引用。 - 补充导入
time模块,避免time.sleep报错。
注意:此方案依赖全局变量,容易引发多测试用例间的状态污染,不推荐用于复杂测试场景。
内容的提问来源于stack exchange,提问作者Vladimir
相关产品推荐
相关产品推荐

