Django自定义对象工厂如何在测试数据库正确创建模型实例
自定义测试对象工厂写入正式库问题修复
问题根因
你的代码有两个核心错误:
- 你在模块顶层定义
dummy_factory字典时,直接调用了get_dummy_object()执行数据库创建操作。这部分代码会在模块被导入时立刻执行,此时Django还未初始化测试数据库环境,连接的是你配置的正式业务数据库,创建的实例自然会写入正式库。 - 字典里存储的是提前创建好的静态模型实例,而非可调用的工厂生成逻辑,所有测试用例拿到的都是模块导入时生成的同一个对象,不仅会污染正式库,还会导致测试之间数据互相干扰,破坏单元测试的隔离性。
修复方案
核心调整原则:只在测试用例执行阶段(即setUp方法运行时)才触发模型实例创建,不要在模块导入阶段执行任何数据库写入操作。
- 顶层工厂映射只存储工厂类实例,不要提前调用生成方法创建对象
- 替换原来静态存储值的
dot_dict,实现属性访问时实时触发实例生成的代理逻辑,保证实例创建动作发生在TestCase已经完成测试数据库切换的阶段 - 每次访问工厂属性都生成全新的模型实例,保证测试数据隔离
修复后的完整代码
工厂定义部分
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
相关产品推荐
相关产品推荐

