Django测试中FactoryBoy访问开发数据库而非测试库的问题
问题成因
你遇到的问题根源是 Python模块的加载时机和FactoryBoy属性的求值时机不匹配:
- 你在定义
ProductFactory的market_category字段时,直接执行了list(MarketplaceCategory.objects.all()),这段代码会在factories.py模块被导入的瞬间就执行完成,此时Django还没有启动测试流程、也没有切换到测试数据库,连接的依然是你的本地开发库,最终生成的选项列表里存的全是开发库的MarketplaceCategory记录,报错里出现的主键25就是开发库中的数据。 - 测试运行时
setUp方法里插入的是测试库的数据,而ProductFactory使用的还是模块导入阶段预生成的旧选项列表,自然会出现外键不存在的错误。
解决方案
有三个常用的修复方案,按需选择即可:
方案1:动态查询当前数据库的分类
使用LazyAttribute让查询逻辑延迟到工厂实例化时才执行,每次生成Product实例都会实时查询当前连接的数据库(测试阶段就是测试库)的分类数据:
# factories.py 修改后 class ProductFactory(DjangoModelFactory): ... # 每次实例化时才随机取当前库中存在的分类 market_category = factory.LazyAttribute(lambda _: MarketplaceCategory.objects.order_by('?').first()) class Meta: model = Product
注意:该方案需要保证调用ProductFactory前,数据库中已存在至少一条MarketplaceCategory记录,否则会返回空值触发非空约束报错。
方案2:显式指定关联对象
如果你的测试场景中需要复用setUp里创建的分类,直接在实例化工厂时手动传入即可,完全规避随机查询的问题:
# tests.py 修改后 def setUp(self) -> None: ... self.marketplace_category = MarketplaceCategoryFactory.create() self.product = ProductFactory( created_by=self.user, market_category=self.marketplace_category # 手动传入已创建的分类 )
方案3:给FuzzyChoice传入可调用对象
如果你希望继续使用FuzzyChoice的语法,可将选项列表包裹为可调用对象,FactoryBoy会在实例化时才执行调用获取最新的选项:
# factories.py 修改后 class ProductFactory(DjangoModelFactory): ... # 传入lambda作为可调用对象,延迟执行查询 market_category = factory.fuzzy.FuzzyChoice(lambda: list(MarketplaceCategory.objects.all())) class Meta: model = Product
内容的提问来源于stack exchange,提问作者Milano
相关产品推荐
相关产品推荐

