Django测试:父类创建实例时如何阻止被Mock的post_save_method执行?
解决Django测试中父类创建实例时无法Mock Model方法的问题
问题原因
类级的@patch.object是在测试方法执行前才会生效,但Django测试的执行流程是:
- 初始化测试类
- 运行
setUp方法(父类的setUp先执行) - 执行测试方法
所以当父类setUp中创建MyModel实例时,post_save_method还是原始方法,自然会执行并打印"oh no!";而测试方法中调用self.my_model.post_save_method()时,patch已经生效,所以返回Mock值,这就导致了现象不一致。
可行方案
方案1:改用setUpClass创建实例
将父类中创建实例的逻辑移到setUpClass(类级初始化方法),因为类级patch会在测试类初始化时就应用Mock,此时setUpClass运行时Mock已经生效:
from django.test import TestCase from .models import MyModel from unittest.mock import patch class BaseTest(TestCase): @classmethod def setUpClass(cls): super().setUpClass() # 此时类级patch已经生效,创建实例时会使用Mock后的方法 cls.my_model = MyModel.objects.create() @patch.object(MyModel, 'post_save_method') class TestMakeObjectInSuper(BaseTest): def test_something(self, post_save_mock): post_save_mock.return_value = "mocked value" print(self.my_model.post_save_method()) # 输出"mocked value",且不会打印"oh no!"
方案2:在子类setUp中先启动Mock再调用父类初始化
子类重写setUp,先通过上下文管理器启动Mock,再调用父类的setUp创建实例,确保创建实例时Mock已经生效:
class BaseTest(TestCase): def setUp(self): self.my_model = MyModel.objects.create() class TestMakeObjectInSuper(BaseTest): def setUp(self): # 先启动Mock并配置返回值 with patch.object(MyModel, 'post_save_method') as self.post_save_mock: self.post_save_mock.return_value = "mocked value" # 此时调用父类setUp,创建实例时会使用Mock后的方法 super().setUp() def test_something(self): print(self.my_model.post_save_method()) # 输出"mocked value",无"oh no!"打印
方案3:修改Model添加跳过参数(无Mock方案)
在Model的save方法中添加开关参数,测试时传入参数跳过post_save_method的执行,无需依赖Mock:
修改Model代码:
class MyModel(models.Model): def save(self, *args, skip_post_save=False, **kwargs): super(MyModel, self).save(*args, **kwargs) # 通过参数控制是否执行后置方法 if not skip_post_save: self.post_save_method() def post_save_method(self): print("oh no!") return "real value"
修改测试父类:
class BaseTest(TestCase): def setUp(self): # 创建实例时传入skip_post_save=True,跳过post_save_method执行 self.my_model = MyModel.objects.create(skip_post_save=True) class TestMakeObjectInSuper(BaseTest): def test_something(self): # 如果需要Mock返回值,依然可以单独patch with patch.object(self.my_model, 'post_save_method') as mock: mock.return_value = "mocked value" print(self.my_model.post_save_method())
内容的提问来源于stack exchange,提问作者Derek Hill
相关产品推荐
相关产品推荐

