FactoryBoy-嵌套工厂/最大深度?

Jas*_*per 9 python django factory-boy

我正在为一个大型Django应用程序编写测试,作为该过程的一部分,我将逐步为Django项目中不同应用程序的所有模型创建工厂。

但是,我在FactoryBoy中遇到了一些令人困惑的行为,在该行为中,似乎似乎SubFactories具有最大深度,超过该深度则不会生成任何实例。

当我尝试运行以下测试时,发生错误:

    def test_subfactories(self):
        """ Verify that the factory is able to initialize """
        user = UserFactory()
        self.assertTrue(user)
        self.assertTrue(user.profile)
        self.assertTrue(user.profile.tenant)

        order = OrderFactory()
        self.assertTrue(order)
        self.assertTrue(order.user.profile.tenant)
Run Code Online (Sandbox Code Playgroud)

最后一行将失败(AssertionError: None is not true),通过调试器运行此测试将显示确实返回order.user.profile.tenant None而不是预期的Tenant实例。

这里涉及很多工厂/模型,但是布局相对简单。

User(django的默认值)和所述Profile模型通过一个OneToOneField,其中(后有些麻烦)由表示连接UserFactoryProfileFactory

@factory.django.mute_signals(post_save)
class ProfileFactory(factory.django.DjangoModelFactory):

    class Meta:
        model = yuza_models.Profile
        django_get_or_create = ('user',)

    user = factory.SubFactory('yuza.factories.UserFactory')
    birth_date = factory.Faker('date_of_birth')
    street = factory.Faker('street_name')
    house_number = factory.Faker('building_number')
    city = factory.Faker('city')
    country = factory.Faker('country')
    avatar_file = factory.django.ImageField(color='blue')
    tenant = factory.SubFactory(TenantFactory)
Run Code Online (Sandbox Code Playgroud)
@factory.django.mute_signals(post_save)
class UserFactory(factory.django.DjangoModelFactory):

    class Meta:
        model = auth_models.User

    username = factory.Sequence(lambda n: "user_%d" % n)
    first_name = factory.Faker('first_name')
    last_name = factory.Faker('last_name')

    email = factory.Faker('email')
    is_staff = False
    is_superuser = False
    is_active = True
    last_login = factory.LazyFunction(timezone.now)

    @factory.post_generation
    def profile(self, create, extracted):
        if not create:
            return
        if extracted is None:
            ProfileFactory(user=self)

Run Code Online (Sandbox Code Playgroud)

TenantFactory下面被表示为SubFactoryProfileFactory上方。

class TenantFactory(factory.django.DjangoModelFactory):
    class Meta:
        model = elearning_models.Tenant

    name = factory.Faker('company')
    slug = factory.LazyAttribute(lambda obj: text.slugify(obj.name))
    name_manager = factory.Faker('name')
    title_manager = factory.Faker('job')
    street = factory.Faker('street_name')
    house_number = factory.Faker('building_number')
    house_number_addition = factory.Faker('secondary_address')
Run Code Online (Sandbox Code Playgroud)

Order被链接到一个User,但它的很多方法调用其领域self.user.profile.tenant

class OrderFactory(factory.DjangoModelFactory):
    class Meta:
        model = Order

    user = factory.SubFactory(UserFactory)
    order_date = factory.LazyFunction(timezone.now)
    price = factory.LazyFunction(lambda: Decimal(random.uniform(1, 100)))
    site_tenant = factory.SubFactory(TenantFactory)
    no_tax = fuzzy.FuzzyChoice([True, False])
Run Code Online (Sandbox Code Playgroud)

同样,测试中的大多数断言都可以通过,所有单独的工厂都可以从其直接的外键关系中初始化获取值。但是,一旦工厂/模型彼此分离了三个步骤,则调用将返回None而不是预期的Tenant实例。

由于我无法在FactoryBoy文档中找到对此行为的任何引用,因此它可能是我的错误,但是到目前为止,我无法确定其来源。有人知道我在做什么错吗?

post_save方法

def create_user_profile(sender, instance, created, **kwargs):
    if created:
        profile = Profile.objects.create(user=instance)
        resume = profile.get_resume()
        resume.initialize()


post_save.connect(create_user_profile, sender=User)
Run Code Online (Sandbox Code Playgroud)

Jas*_*per 0

正如我在评论中提到的,我发现了问题的根源:post-save链接到的方法UserProfile(我已在帖子中包含了代码)。

这个post-save方法创建了一个Profileoncreation User@factory.django.mute_signals我通过在UserFactory和上使用装饰器来解释这个信号ProfileFactory

我曾假设任何调用Order.user都会触发UserFactory已经包含在装饰器中的 ,但这并不是事实证明是错误的假设。只有当我也将装饰应用到时OrderFactory,测试才通过。

因此,@factory.django.mute_signals装饰器不仅应该用在受这些信号影响的工厂上,还应该用在任何将这些工厂用作SubFactory!