Pytest Fixture修改另一Fixture返回实例问题问询
Pytest Fixture 共享实例导致测试失败的问题解析
问题重现
以下代码中,test_1执行通过,但test_2断言失败:
import pytest @pytest.fixture def new_object(): return list() @pytest.fixture def a_string(new_object): # 修改该对象实例 new_object.append(1) return "a string" def test_1(new_object): assert len(new_object) == 0 def test_2(a_string, new_object): assert len(new_object) == 0
报错信息:
def test_2(a_string, new_object): > assert len(new_object) == 0 E assert 1 == 0 E + where 1 = len([1]) tests/test_pytest_list.py:21: AssertionError ================================================ short test summary info ================================================= FAILED tests/test_pytest_list.py::test_2 - assert 1 == 0 ============================================== 1 failed, 1 passed in 0.36s ===============================================
你疑惑的点在于:明明官方文档说fixture默认作用域是function(每个测试函数调用一次),为什么a_string里修改的new_object会影响test_2中直接引用的new_object?
核心原因
pytest的fixture默认作用域function的含义是:每个测试函数执行时,同一个fixture只会被初始化一次。也就是说,在同一个测试函数的上下文里,不管是测试函数直接引用该fixture,还是其他fixture依赖该fixture,都会复用同一个实例——pytest会对fixture实例做缓存,在当前测试函数的生命周期内只创建一次。
具体到test_2:
- 测试函数依赖
a_string和new_object - 初始化
a_string时,会先初始化new_object并修改它(添加元素1) - 当
test_2直接引用new_object时,拿到的是已经被a_string修改过的同一个实例,因此断言失败
解决方案
如果需要每次依赖new_object都拿到全新的实例,可以改用工厂函数模式:
import pytest @pytest.fixture def new_object(): # 返回一个工厂函数,每次调用都会生成新列表 def _create_list(): return list() return _create_list @pytest.fixture def a_string(new_object): # 调用工厂函数获取新实例 obj = new_object() obj.append(1) return "a string" def test_1(new_object): obj = new_object() assert len(obj) == 0 def test_2(a_string, new_object): obj = new_object() assert len(obj) == 0
这样,无论是a_string还是test_2,调用new_object()都会得到独立的列表实例,不会互相影响。
补充案例解析
你更新的案例同样是因为同一个测试函数内的fixture共享实例导致的问题:
@pytest.fixture def new_object(): """我想要测试该类的实例""" return list() @pytest.fixture def case_1(new_object): new_object.append(1) return new_object @pytest.fixture def case_2(new_object): new_object.append(2) return new_object def test_cases(case_1, case_2): assert sum(case_1) + sum(case_2) == 3 # 失败:assert (3 + 3) == 3
在test_cases的执行上下文里,new_object只被初始化一次:
- 初始化
case_1时,给列表添加1 - 初始化
case_2时,给同一个列表添加2,此时列表变为[1,2] case_1和case_2指向同一个列表,sum都是3,因此断言失败
用工厂函数修复后:
import pytest @pytest.fixture def new_object(): def _create_list(): return list() return _create_list @pytest.fixture def case_1(new_object): obj = new_object() obj.append(1) return obj @pytest.fixture def case_2(new_object): obj = new_object() obj.append(2) return obj def test_cases(case_1, case_2): assert sum(case_1) + sum(case_2) == 3 # 现在case_1是[1],case_2是[2],断言通过
内容的提问来源于stack exchange,提问作者Bill
相关产品推荐
相关产品推荐

