如何在unittest.mock中复用带pytest fixture和参数的Mock函数?
解决pytest fixture与unittest.mock.patch.object结合的复用函数参数传递问题
我在编写单元测试时,需要结合使用pytest fixture和unittest.mock.patch.object,希望复用一个被多个测试调用的函数——这个函数既依赖pytest fixture(作为第一个参数),又需要接收额外参数。
当前尝试的代码如下:
import pandas as pd import pytest import os from unittest.mock import patch @pytest.fixture() def rootdir(): return os.path.dirname(os.path.abspath(__file__)) def my_mock_ret(rootdir, number): print(f"{rootdir}_{number}") return f"{rootdir}_{number}" def test_simple(rootdir): a = pd.DataFrame() with patch.object(a, 'to_csv', lambda x: my_mock_ret(rootdir, x)) as _: a.to_csv(rootdir)
但这种lambda的写法无法正常工作。如果把my_mock_ret定义在测试函数内部可以正常运行,但我希望复用这个函数,不想这么做:
import pandas as pd import pytest import os from unittest.mock import patch @pytest.fixture() def rootdir(): return os.path.dirname(os.path.abspath(__file__)) def test_simple(rootdir): def my_mock_ret(number): print(f"{rootdir}_{number}") return f"{rootdir}_{number}" a = pd.DataFrame() with patch.object(a, 'to_csv', my_mock_ret) as _: a.to_csv(rootdir)
方法1:用functools.partial绑定fixture参数
通过functools.partial预先把fixture参数绑定到复用函数上,生成一个只接收额外参数的新函数,完美适配patch的需求:
import pandas as pd import pytest import os from unittest.mock import patch from functools import partial @pytest.fixture() def rootdir(): return os.path.dirname(os.path.abspath(__file__)) def my_mock_ret(rootdir, number): print(f"{rootdir}_{number}") return f"{rootdir}_{number}" def test_simple(rootdir): a = pd.DataFrame() # 绑定rootdir参数,生成仅需number的函数 bound_mock = partial(my_mock_ret, rootdir) with patch.object(a, 'to_csv', bound_mock) as _: a.to_csv(rootdir)
方法2:封装为带参数的fixture
如果这个mock函数需要在多个测试中复用,可以把它封装成依赖rootdir的参数化fixture,直接在测试中调用:
import pandas as pd import pytest import os from unittest.mock import patch @pytest.fixture() def rootdir(): return os.path.dirname(os.path.abspath(__file__)) @pytest.fixture def my_mock_ret_fixture(rootdir): def _mock(number): print(f"{rootdir}_{number}") return f"{rootdir}_{number}" return _mock def test_simple(my_mock_ret_fixture, rootdir): a = pd.DataFrame() with patch.object(a, 'to_csv', my_mock_ret_fixture) as _: a.to_csv(rootdir) def test_another(my_mock_ret_fixture): # 其他测试直接复用该fixture b = pd.DataFrame() with patch.object(b, 'to_csv', my_mock_ret_fixture) as _: b.to_csv("another_test_path")
方法3:修正lambda的参数兼容
如果坚持使用lambda,需要兼容pd.DataFrame.to_csv的完整参数签名(它会传递多个关键字参数),用*args接收所有位置参数即可:
def test_simple(rootdir): a = pd.DataFrame() # 用*args接收所有位置参数,取第一个作为number with patch.object(a, 'to_csv', lambda *args: my_mock_ret(rootdir, args[0])) as _: a.to_csv(rootdir)
内容的提问来源于stack exchange,提问作者itscarlayall
相关产品推荐
相关产品推荐

