pytest-dev / pytest-factoryboy

factory_boy integration the pytest runner
MIT License
359 stars 42 forks source link

Problems with Maybe / Trait when defined in Params #126

Open MRigal opened 3 years ago

MRigal commented 3 years ago

Especially with FactoryBoy 3.2. See failing tests in https://github.com/pytest-dev/pytest-factoryboy/pull/125

elgin9507 commented 3 years ago

I'm still facing this issue:

AttributeError: 'Maybe' object has no attribute 'call'


request = <SubRequest 'timesheet' for <Function test_can_include_date>>

    def deferred(request):
>       declaration.call(instance, step, context)
E       AttributeError: 'Maybe' object has no attribute 'call'

../.venv/lib/python3.9/site-packages/pytest_factoryboy/fixture.py:294: AttributeError

Debugging info:

(Pdb) declaration
Maybe(<SelfAttribute('has_entries', default=False)>, yes=<factory.declarations.RelatedFactoryList object at 0x7ffb03b409d0>, no=<factory.declarations.Skip object at 0x7ffb23dcc160>)
(Pdb) type(step)
<class 'factory.builder.BuildStep'>
(Pdb) context
PostGenerationContext(value_provided=True, value=Maybe(<SelfAttribute('has_entries', default=False)>, yes=<factory.declarations.RelatedFactoryList object at 0x7ffb03b409d0>, no=<factory.declarations.Skip object at 0x7ffb23dcc160>), extra={})

Versions in my environment:

factory-boy==3.2.0
pytest==6.2.5
pytest-django==4.4.0
pytest-factoryboy==2.1.0
satodaiki commented 2 years ago

I also encountered the same event.

The factory I defined is the one that contains the RelatedFactory in the Trait.

as follows:

import factory

class UserFactory(factory.Factory):
    class Meta:
        model = User

    id = 1

class BookFactory(factory.Factory):
    class Meta:
        model = Book

    class Params:
        user_r = factory.Trait(
            users=factory.RelatedFactoryList("factories.UserFactory"),
            factory_related_name="id",
            size=10,
        )

    name = "book name"

I am not sure about the details of the implementation, but it worked locally by modifying the conditional expression for the decl object in the following location.

https://github.com/pytest-dev/pytest-factoryboy/blob/78844d9f7b97047b94348066933f73f75854839c/pytest_factoryboy/fixture.py#L403

if isinstance(decl, (factory.RelatedFactory, factory.Maybe)):

If the corrections above are sufficient to address the issue, then please fix it!

skarzi commented 2 years ago

It's really hard to properly handle Maybe (and also Trait because under the hood it uses as_declarations to override affected fields with Maybe - reference). To properly generate fixtures for Maybe we need to somehow generate a proper dependencies chain, which is really hard, because Maybe.decider can be computed from anything using some declaration, however, the most commonly used are probably the following ones:

Maybes can be also nested, which makes implementation even more complicated.

If you have any idea how to add support for Maybe and/or Params to our code, please share it here!