pytest参数化测试传递mock fixture触发AttributeError问题
在pytest参数化测试中传递mock fixture的问题解决
你遇到的AttributeError: 'function' object has no attribute 'assert_called_once_with'错误,核心原因是参数化列表里直接传入的mock_adam、mock_RMSprop是fixture的函数对象,而非pytest实例化后生成的mock对象。fixture函数本身不具备mock的断言方法,因此调用assert_called_once_with会触发报错。
完全可以在参数化测试中使用mock fixture,只需调整传递方式,下面提供两种可行方案:
方案一:用request.getfixturevalue获取fixture实例
将参数化中的fixture对象替换为fixture名称字符串,在测试用例中通过request对象的getfixturevalue方法获取实际的mock实例:
import pytest from contextlib import contextmanager from unittest import mock from my_module import get_optimizer @contextmanager def does_not_raise(): yield # 去掉autouse=True,避免不必要的mock自动生效 @pytest.fixture def mock_adam(): with mock.patch("my_module.optimizers.Adam") as mocker: yield mocker @pytest.fixture def mock_RMSprop(): with mock.patch("my_module.optimizers.RMSprop") as mocker: yield mocker class TestGetOptimizers: @pytest.mark.parametrize( "mock_fixture_name, optimizer_name, learning_rate, clipnorm, expectation", [ ("mock_adam", "adam", "0.001", "1.1", does_not_raise()), ("mock_RMSprop", "rmsprop", "0.001", "1.1", does_not_raise()), ], ) def test_get_optimizer(self, request, mock_fixture_name, optimizer_name, learning_rate, clipnorm, expectation): # 获取实际的mock实例 mock_optimizer = request.getfixturevalue(mock_fixture_name) with expectation: get_optimizer( optimizer_name=optimizer_name, learning_rate=learning_rate, clipnorm=clipnorm, ) mock_optimizer.assert_called_once_with(lr=learning_rate, clipnorm=clipnorm)
方案二:使用参数化fixture(更简洁)
将多个mock fixture合并为一个参数化fixture,直接绑定优化器名称和mock路径,测试用例只需依赖这个fixture即可:
import pytest from contextlib import contextmanager from unittest import mock from my_module import get_optimizer @contextmanager def does_not_raise(): yield # 参数化fixture,每个参数对应一个优化器的mock路径和名称 @pytest.fixture(params=[ ("adam", "my_module.optimizers.Adam"), ("rmsprop", "my_module.optimizers.RMSprop") ]) def mock_optimizer(request): optimizer_name, patch_target = request.param with mock.patch(patch_target) as mocker: yield mocker, optimizer_name class TestGetOptimizers: @pytest.mark.parametrize( "learning_rate, clipnorm, expectation", [ ("0.001", "1.1", does_not_raise()), # 可添加更多参数组合 ], ) def test_get_optimizer(self, mock_optimizer, learning_rate, clipnorm, expectation): mock_obj, optimizer_name = mock_optimizer with expectation: get_optimizer( optimizer_name=optimizer_name, learning_rate=learning_rate, clipnorm=clipnorm, ) mock_obj.assert_called_once_with(lr=learning_rate, clipnorm=clipnorm)
关键注意点
- 原代码中
autouse=True会导致所有测试用例自动触发mock,建议去掉,改为按需调用,避免干扰其他测试。 - 参数化测试中传递fixture时,必须获取pytest实例化后的对象,而非直接传递fixture函数本身。
内容的提问来源于stack exchange,提问作者Devester
相关产品推荐
相关产品推荐

