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

Django自定义对象工厂如何在测试数据库正确创建模型实例

自定义测试对象工厂写入正式库问题修复

问题根因

你的代码有两个核心错误:

  • 你在模块顶层定义dummy_factory字典时,直接调用了get_dummy_object()执行数据库创建操作。这部分代码会在模块被导入时立刻执行,此时Django还未初始化测试数据库环境,连接的是你配置的正式业务数据库,创建的实例自然会写入正式库。
  • 字典里存储的是提前创建好的静态模型实例,而非可调用的工厂生成逻辑,所有测试用例拿到的都是模块导入时生成的同一个对象,不仅会污染正式库,还会导致测试之间数据互相干扰,破坏单元测试的隔离性。

修复方案

核心调整原则:只在测试用例执行阶段(即setUp方法运行时)才触发模型实例创建,不要在模块导入阶段执行任何数据库写入操作。

  1. 顶层工厂映射只存储工厂类实例,不要提前调用生成方法创建对象
  2. 替换原来静态存储值的dot_dict,实现属性访问时实时触发实例生成的代理逻辑,保证实例创建动作发生在TestCase已经完成测试数据库切换的阶段
  3. 每次访问工厂属性都生成全新的模型实例,保证测试数据隔离

修复后的完整代码

工厂定义部分

from abc import ABC
from users.models import User
from employees.models import Employee
from .dummy_data import(
    user_data,
    employee_data,
)

class DummyObjectFactory(ABC):
    """测试用虚拟对象生成基类"""
    def get_dummy_object(self):
        """返回生成的测试模型实例"""
        raise NotImplementedError

class DummyUser(DummyObjectFactory):
    def get_dummy_object(self) -> User:
        return User.objects.create_user(**user_data)

class DummyEmployee(DummyObjectFactory):
    def get_dummy_object(self) -> Employee:
        user = DummyUser().get_dummy_object()
        return Employee.objects.create(**employee_data, user=user)

# 仅存储工厂实例,不提前执行对象创建
_factory_mapping = {
    "User": DummyUser(),
    "Employee": DummyEmployee(),
}

点调用代理实现

替换原来的静态dot_dict封装,实现访问属性时实时生成实例:

class DummyFactoryAccessor:
    def __init__(self, factory_map):
        self._factory_map = factory_map

    def __getattr__(self, attr_name):
        if attr_name not in self._factory_map:
            raise AttributeError(f"无对应{attr_name}的测试对象工厂")
        # 访问属性时才调用生成方法,此时已处于测试数据库环境
        return self._factory_map[attr_name].get_dummy_object()

dummy_factory = DummyFactoryAccessor(_factory_mapping)

测试用例部分

原有测试用例代码无需修改,即可正常运行:

class TestEmployeesListView(TestCase):
    def setUp(self):
        self.test_user = dummy_factory.User
        self.test_employee = dummy_factory.Employee
        self.client = Client()

    def test_listview_deny_anonymous_user(self):
        response = self.client.get(reverse('employees:employees-list'))
        self.assertRedirects(response, '/login/?next=/employees/')

额外说明

  • 修复后每次调用dummy_factory.模型名都会生成全新的独立实例,每个测试用例的setUp都会拿到专属测试数据,测试执行完成后Django TestCase会自动回滚测试库事务,不会残留脏数据,也不会对正式库产生任何写入操作。
  • 如果需要多个模型共用同一个关联对象(比如多个Employee绑定同一个User),可以在setUp中先生成共享的User实例,再手动传入对应工厂的生成方法,避免重复创建冗余数据。

内容的提问来源于stack exchange,提问作者user5622143

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 23:54:22