Factoryboy无法生成数据库依赖字段,django-cities-light测试场景如何解决?
问题根源
你写的工厂类存在两个核心问题:
FuzzyChoice(Country.objects.all())会在类定义阶段就执行Country.objects.all()查询,测试环境类加载时数据库还没导入数据,拿到空序列直接触发报错,or None的兜底逻辑完全不会生效。- 没有处理测试环境无基础地理数据的 fallback 逻辑,关联字段依赖上层关联对象存在,空值会触发连锁报错。
方案1:测试环境预填充地理数据(推荐贴近真实业务场景)
先在测试初始化阶段导入django-cities-light的基础数据集,可以用django的setUpTestData或者pytest全局fixture执行导入逻辑,再修改工厂类把查询延迟到实例生成阶段:
class UserProfileFactory(DjangoModelFactory): class Meta: model = UserProfile # 传入callable让FuzzyChoice在实例生成时再执行查询 birth_country = factory.fuzzy.FuzzyChoice(lambda: Country.objects.all()) # 增加空值兜底,避免上层字段为空时报错 birth_region = factory.LazyAttribute( lambda o: o.birth_country.region_set.order_by("?").first() if o.birth_country else None ) birth_subregion = factory.LazyAttribute( lambda o: o.birth_region.subregion_set.order_by("?").first() if o.birth_region else None ) birth_city = factory.LazyAttribute( lambda o: o.birth_region.city_set.order_by("?").first() if o.birth_region else None )
方案2:完全自包含的测试工厂(不需要提前导入数据,适合单元测试)
先给地理模型创建对应的工厂类,没有现成数据时自动生成假数据,适配任何环境:
# 先定义基础地理数据工厂,按django-cities-light的模型必填字段补充参数即可 class CountryFactory(DjangoModelFactory): class Meta: model = Country name = factory.Faker("country") code2 = factory.Faker("pystr", min_chars=2, max_chars=2, prefix="", suffix="", letters="ABCDEFGHIJKLMNOPQRSTUVWXYZ") # 其他必填字段按需补充 class RegionFactory(DjangoModelFactory): class Meta: model = Region name = factory.Faker("state") country = factory.SubFactory(CountryFactory) class SubRegionFactory(DjangoModelFactory): class Meta: model = SubRegion name = factory.Faker("state") region = factory.SubFactory(RegionFactory) class CityFactory(DjangoModelFactory): class Meta: model = City name = factory.Faker("city") region = factory.SubFactory(RegionFactory) # 改造后的UserProfileFactory,有现成数据用现成的,没有就自动创建 class UserProfileFactory(DjangoModelFactory): class Meta: model = UserProfile birth_country = factory.LazyFunction( lambda: Country.objects.order_by("?").first() or CountryFactory() ) birth_region = factory.LazyAttribute( lambda o: o.birth_country.region_set.order_by("?").first() or RegionFactory(country=o.birth_country) ) birth_subregion = factory.LazyAttribute( lambda o: o.birth_region.subregion_set.order_by("?").first() or SubRegionFactory(region=o.birth_region) ) birth_city = factory.LazyAttribute( lambda o: o.birth_region.city_set.order_by("?").first() or CityFactory(region=o.birth_region) )
可选优化
如果不想大量使用LazyAttribute,可以把关联字段的生成逻辑封装为PostGeneration钩子,或者自定义factory_boy的Provider,复用逻辑。
内容的提问来源于stack exchange,提问作者aladagemre
相关产品推荐
相关产品推荐

