第四章:夹具作用域控制
在 UI 自动化测试中,夹具的作用域决定了夹具实例的创建与销毁时机。错误的作用域选择会导致不必要的对象创建(降低性能)或状态污染(降低稳定性)。本章深入剖析方法级、类级、会话级三种作用域的原理、选择策略与状态共享实践。
作用域级别核心原理
| 作用域级别 | 配置生命周期 | UI 测试典型用例 | 性能(Ops/秒) | 隔离级别 |
|---|---|---|---|---|
| 方法级(function) | 每个测试方法前创建,方法结束后销毁 | 需要完全清洁状态的用例,如跨页面权限验证 | 低(浏览器实例反复启动) | ★★★★★ |
| 类级(class) | 每个测试类前创建,类中所有方法结束后销毁 | 同一页面模块内的批量功能验证 | 中(浏览器复用一次) | ★★★★☆ |
| 会话级(session) | 整个测试执行前创建,全部测试结束后销毁 | 登录状态保持、全局配置加载 | 高(无重复创建开销) | ★★☆☆☆ |
实现流程与代码示例
在 pytest 中,通过 @pytest.fixture(scope='...') 控制作用域。以下是一个典型的 UI 自动化夹具配置,分别展示了三种作用域的使用方式。
# conftest.py
import pytest
from selenium import webdriver
# 方法级作用域:每个测试函数启动一个新浏览器实例
@pytest.fixture(scope='function')
def driver_function():
print("\n[SETUP] 方法级:启动浏览器")
driver = webdriver.Chrome()
yield driver
print("[TEARDOWN] 方法级:关闭浏览器")
driver.quit()
# 类级作用域:每个测试类只启动一次浏览器
@pytest.fixture(scope='class')
def driver_class(request):
print("\n[SETUP] 类级:启动浏览器")
driver = webdriver.Chrome()
request.cls.driver = driver # 将 driver 注入到类中
yield
print("[TEARDOWN] 类级:关闭浏览器")
driver.quit()
# 会话级作用域:整个测试运行期间只登录一次
@pytest.fixture(scope='session')
def user_():
print("[SETUP] 会话级:获取用户 ")
= login_to_app()
yield
print("[TEARDOWN] 会话级:清除 ")
logout()
# test_demo.py
import pytest
# 使用类级作用域共享 driver
@pytest.mark.usefixtures('driver_class')
class TestDashboard:
def test_title(self):
self.driver.get("http://example.com/dashboard")
assert "Dashboard" in self.driver.title
def test_widget(self):
# 这里依然使用同一个 driver 实例
widget = self.driver.find_element("id", "widget1")
assert widget.is_displayed()
调试作用域执行顺序
使用 pytest --setup-show 命令可以清晰地看到夹具的创建和销毁顺序,帮助验证作用域设置是否符合预期。
# 执行命令
pytest test_demo.py --setup-show -v
# 输出示例(简化)
# FIXTURES: conftest::user_ (scope='session') --> 运行一次
# FIXTURES: conftest::driver_class (scope='class') --> 类运行前创建
# test_demo.py::TestDashboard::test_title (fixtures used: driver_class, user_)
# test_demo.py::TestDashboard::test_widget (fixtures used: driver_class, user_)
# FIXTURES: conftest::driver_class (scope='class') --> 类运行后销毁
# FIXTURES: conftest::user_ (scope='session') --> 所有类运行后销毁
常见错误与性能稳定性优化
警告:作用域选择不当导致的状态泄漏
如果在会话级作用域中存储了可修改的对象(如列表、字典或页面对象实例),并且测试方法之间没有清理这些对象,就会产生测试间依赖。例如:
# 错误的做法
class TestSuite:
shared_items = [] # 被所有测试共享
def test_add_item(self):
TestSuite.shared_items.append("item1")
assert len(TestSuite.shared_items) == 1 # 可能因为前序测试而失败
解决方法:只在会话级作用域中保存不可变状态(如 、配置字典),或者确保每次修改后都进行清理。
实践建议:UI 测试的黄金法则
在 UI 自动化测试中,类级作用域通常是平衡性能与稳定性的最佳选择。它避免了方法级反复启动浏览器的巨大开销,同时相比会话级又大大降低了状态污染的复杂度。如果某个测试确实需要全新的浏览器状态,再单独为其指定方法级作用域。
使用作用域共享状态的最佳实践
- 只读共享:在会话级作用域中共享配置、环境变量、数据库连接,这些对象在使用过程中不应被测试修改。
- 写时复制:如果需要在类内部修改共享状态(例如缓存一个页面对象),请在
setup_method中进行深拷贝,避免影响其他测试。 - 显式清理:对于每个作用域,都应在
teardown中显式恢复状态,确保下一个测试用例从已知的基线开始。
# conftest.py - 安全共享状态示例
@pytest.fixture(scope='session')
def config():
# 返回不可变配置
return {
'base_url': 'http://example.com',
'timeout': 30
}
@pytest.fixture(scope='class')
def driver_with_clean_state(config, request):
driver = webdriver.Chrome()
driver.get(config['base_url'])
# 清理 cookies,确保每个类开始时都是干净的
driver.delete_all_cookies()
yield driver
driver.quit()