You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 06:05:18