pytest-bdd+Selenium报错AttributeError:NoneType无execute_script属性
核心原因分析
出现AttributeError: 'NoneType' object has no attribute 'execute_script',说明第二次调用browser fixture时拿到的是None,常见触发场景有以下几种:
Fixture创建WebDriver失败
如果conftest.py中创建WebDriver的代码抛出异常(比如ChromeDriver路径错误、浏览器版本不兼容),会导致yield返回None,首次调用可能因为缓存或异常被吞没暂时正常,后续步骤暴露问题。步骤函数意外销毁/覆盖实例
- 在
login步骤中手动调用了browser.quit(),导致WebDriver会话关闭; - 错误地对
browser局部变量重新赋值(比如browser = None),虽然不会影响fixture本身,但如果后续步骤依赖该局部变量会出问题。
- 在
Fixture作用域与测试流程不匹配
如果browserfixture的作用域设为function,但你的测试场景被拆分为多个独立测试函数(比如多个Scenario),前一个场景的teardown执行了driver.quit(),后一个场景创建driver失败就会返回None。其他fixture提前销毁实例
如果有依赖browser的其他fixture(比如登录fixture)中提前执行了driver.quit(),会导致后续步骤拿到已失效的实例。
针对性解决方案
1. 修正browser fixture的定义
确保fixture正确创建并返回WebDriver实例,同时统一处理销毁逻辑:
# conftest.py import pytest from selenium import webdriver @pytest.fixture(scope="function") def browser(): driver = None try: # 初始化WebDriver,可根据需要配置选项 driver = webdriver.Chrome() driver.implicitly_wait(10) yield driver except Exception as e: # 打印创建失败信息,方便排查 print(f"WebDriver创建失败: {str(e)}") raise finally: # 确保无论是否异常,都销毁实例 if driver: driver.quit()
2. 检查步骤函数的代码逻辑
确保所有步骤函数中没有提前销毁或覆盖browser实例:
错误示例:
@given("用户已登录系统") def login(browser): browser.get("/login") # 错误操作:提前销毁实例 browser.quit() # 或者错误赋值 browser = None
正确示例:
@given("用户已登录系统") def login(browser): browser.get("/login") # 正常执行登录操作 browser.find_element("id", "username").send_keys("test_user") browser.find_element("id", "password").send_keys("test_pwd") browser.find_element("id", "login_btn").click()
3. 验证测试场景的结构
确保login和set_map_area_and_center步骤属于同一个Scenario,保证共享同一个browser实例:
Feature: 地图操作功能 Scenario: 登录后配置地图参数 Given 用户已登录系统 When 设置地图区域和中心点 Then 地图应正确显示指定区域
如果是多个独立Scenario,确保每个Scenario的browser实例都能正常创建。
4. 调试Fixture生命周期
在fixture和步骤中添加日志,追踪实例的创建和销毁时机:
# conftest.py中修改fixture @pytest.fixture(scope="function") def browser(): print(f"创建WebDriver实例,ID: {id(driver)}") driver = webdriver.Chrome() yield driver print(f"销毁WebDriver实例,ID: {id(driver)}") driver.quit() # 步骤函数中添加打印 @given("用户已登录系统") def login(browser): print(f"Login步骤使用的browser ID: {id(browser)}") @when("设置地图区域和中心点") def set_map_area_and_center(browser): print(f"Set Map步骤使用的browser ID: {id(browser)}") if not browser: print("警告:browser实例为None")
通过日志可以确认:
- 两个步骤是否使用同一个实例ID;
- 实例是否在步骤执行前就被销毁。
内容的提问来源于stack exchange,提问作者Mardjacku

