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

Django测试:父类创建实例时如何阻止被Mock的post_save_method执行?

解决Django测试中父类创建实例时无法Mock Model方法的问题

问题原因

类级的@patch.object是在测试方法执行前才会生效,但Django测试的执行流程是:

  1. 初始化测试类
  2. 运行setUp方法(父类的setUp先执行)
  3. 执行测试方法

所以当父类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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 02:06:22