如何在Python中覆盖/绕过测试用的Mock装饰器?
问题场景
我们的测试运行器全局Mock了外部服务(以Stripe为例)的方法,代码如下:
@mock.patch('services.Stripe.sync_customers', side_effect=mock_sync_customers) def run_tests(self, test_labels, extra_tests=None, *args, **kwargs): return super().run_tests(test_labels, extra_tests, **kwargs)
现在需要让特定测试用例test_customers调用实际的API方法,而非Mock实现,同时不想给其他所有测试逐个添加装饰器(易出错)。
解决方案
方案1:在目标测试中临时恢复原方法(最简洁)
利用unittest.mock的上下文管理器,在测试方法内部临时覆盖全局Mock,恢复原方法的执行:
from unittest import mock from services import Stripe from django.test import TestCase class YourTestCase(TestCase): def test_customers(self): # 获取原始方法 original_sync = Stripe.sync_customers # 临时替换Mock为原始方法 with mock.patch('services.Stripe.sync_customers', side_effect=original_sync): Company.objects.create(**customer_data) # 这里会调用实际API # 后续添加你的断言逻辑
也可以用装饰器形式(适合不需要动态逻辑的测试):
from unittest import mock from services import Stripe class YourTestCase(TestCase): @mock.patch('services.Stripe.sync_customers', side_effect=Stripe.sync_customers) def test_customers(self, _): Company.objects.create(**customer_data) # 断言逻辑
方案2:修改测试运行器,根据测试属性跳过Mock
如果有多个测试需要使用真实服务,可以给这些测试标记特殊属性,然后在run_tests中判断是否跳过全局Mock:
- 给测试方法添加标记:
class YourTestCase(TestCase): def test_customers(self): self.use_real_stripe = True Company.objects.create(**customer_data)
- 修改测试运行器逻辑:
def run_tests(self, test_labels, extra_tests=None, *args, **kwargs): # 检查要运行的测试是否包含需要真实服务的用例 use_real_stripe = False # 遍历测试用例(简化逻辑,实际需根据测试框架调整) for test_case in self.get_test_cases(test_labels, extra_tests): if hasattr(test_case, 'use_real_stripe') and test_case.use_real_stripe: use_real_stripe = True break if use_real_stripe: # 不应用Mock,直接运行测试 return super().run_tests(test_labels, extra_tests, **kwargs) else: # 应用全局Mock with mock.patch('services.Stripe.sync_customers', side_effect=mock_sync_customers): return super().run_tests(test_labels, extra_tests, **kwargs)
方案3:临时停止全局Mock并恢复
如果全局Mock是通过装饰器添加的,可以在测试方法中获取Mock对象,临时停止并在测试结束后恢复:
class YourTestCase(TestCase): def test_customers(self): # 获取全局Mock对象 mock_sync = mock.patch.object(services.Stripe, 'sync_customers').mock # 停止Mock,恢复原方法 mock_sync.stop() try: Company.objects.create(**customer_data) # 断言逻辑 finally: # 必须恢复Mock,避免影响其他测试 mock_sync.start()
内容的提问来源于stack exchange,提问作者creyD
相关产品推荐
相关产品推荐

