单测试用例多mock.patch实现方案及fixture问题咨询
单测试用例多mock实现方案及fixture mock失效修复
堆叠@mock.patch之外的可行写法
除了你当前使用的装饰器堆叠写法,unittest.mock还支持两种常用的多mock实现方式,适配不同场景:
- 上下文管理器写法:适合mock仅需在部分代码段生效的场景,不需要在测试方法参数里按顺序接收mock对象,嵌套逻辑更直观
from unittest import mock from django.test import TestCase # 对应你使用的TestCase基类 class TestFunction(TestCase): def test_function(self): # 多上下文管理器用逗号分隔,顺序和装饰器逻辑一致 with mock.patch('path.to.value1', return_value='value1') as mock_val1, \ mock.patch('path.to.value2', return_value='value2') as mock_val2, \ mock.patch('path.to.value3', side_effect=value3) as mock_val3: # 块内mock生效,可直接调用、配置mock断言规则 response = function_being_tested(payload) assert response.status_code == 200 # 退出with块后mock自动还原,不会污染其他测试逻辑
- 手动start/stop写法:适合多个测试方法复用同一套mock逻辑的场景,配合setUp/tearDown统一管理
from unittest import mock class TestFunction(TestCase): def setUp(self): # 前置阶段统一启动mock self.mock_val1 = mock.patch('path.to.value1', return_value='value1').start() self.mock_val2 = mock.patch('path.to.value2', return_value='value2').start() self.mock_val3 = mock.patch('path.to.value3', side_effect=value3).start() def test_function(self): response = function_being_tested(payload) assert response.status_code == 200 def tearDown(self): # 测试结束统一停止所有mock,避免全局污染 mock.patch.stopall()
pytest fixture+monkeypatch失效修复
你之前写的fixture方案不生效,核心是三个问题:
- 混用
unittest.TestCase和pytest fixture时,默认配置下pytest不会给unittest风格的测试类注入fixture monkeypatch.setattr传参错误:如果被mock的目标是可调用对象(函数、方法),第二个参数不能直接传固定值,必须传入Mock对象包装的可调用实例,否则调用时会触发「对象不可调用」报错- 没有遵循mock路径规则:mock路径必须指向被测试文件中导入该对象的位置,而非对象定义的源文件位置,该规则对所有mock写法通用
修复后可运行的fixture方案
推荐直接用纯pytest写法(去掉TestCase继承),兼容性最好:
import pytest from unittest.mock import Mock # 纯pytest测试类不需要继承unittest.TestCase class TestUpdateProduct: def test_function(self, mock_common_deps): # fixture自动完成所有mock注入,直接执行业务逻辑即可 response = function_being_tested(payload) assert response.status_code == 200 # 可以通过fixture返回的mock对象做调用断言、动态改返回值 mock_common_deps['value1'].assert_called_once() @pytest.fixture() def mock_common_deps(monkeypatch): # 先构造对应mock实例 mock_val1 = Mock(return_value='value1') mock_val2 = Mock(return_value='value2') mock_val3 = Mock(side_effect=value3) # value3为你预定义的Response构造逻辑 # 执行替换 monkeypatch.setattr('path.to.value1', mock_val1) monkeypatch.setattr('path.to.value2', mock_val2) monkeypatch.setattr('path.to.value3', mock_val3) # 返回mock对象供测试用例调用 yield { 'value1': mock_val1, 'value2': mock_val2, 'value3': mock_val3 } # monkeypatch会在fixture生命周期结束后自动还原所有替换,无需手动清理
如果必须保留unittest.TestCase继承,只需要给测试类加usefixtures标记即可,不需要在测试方法参数里声明fixture:
import pytest from unittest import TestCase @pytest.mark.usefixtures("mock_common_deps") class TestUpdateProduct(TestCase): def test_function(self): response = function_being_tested(payload) assert response.status_code == 200
内容的提问来源于stack exchange,提问作者Risker
相关产品推荐
相关产品推荐

