pytest-dev / pytest-factoryboy

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

Parametrized traits are not called from fixture in overriden Factory #98

Open TauPan opened 4 years ago

TauPan commented 4 years ago

The general way to reproduce this is:

  1. I have a Django Model with a method
  2. I create a factory for that model with factory.django.DjangoModelFactory
  3. I register a trait using factory.PostGenerationMethodCall
  4. I make a subclass of that factory
  5. I register a factory for that subclass

If I use the factory in a test, the method will not be called.

If I make a pytest fixture manually, calling the overridden factory directly, the method is called.

Since it's an involved case, I'll try to come up with a minimal example to reproduce this on github and reference this issue.

TauPan commented 4 years ago

Here's the repository: https://github.com/TauPan/pytest-factoryboy-reproduce-98

If I run the tests with tox -e py36-pytestlatest-django22, 4 pass and 4 fail. The failing ones are the ones relying on the overridden do_calculate parameter in a fixture created with register. The 2 last tests rely on a manually created pytest fixture based on the same factories and succeed.

Here are the travis results (currently still in progress): https://travis-ci.com/github/TauPan/pytest-factoryboy-reproduce-98

FAILED django_project/tests.py::test_do_calculate - assert None == 3
FAILED django_project/tests.py::test_thing_3_plus_4 - assert None == 7
FAILED django_project/tests.py::test_do_calculate_arg - assert None == 3
FAILED django_project/tests.py::test_thing_3_plus_4_arg - assert 1 == 3
TauPan commented 4 years ago

An alternative way to reproduce the same behaviour is:

modified   django_project/factories.py
@@ -1,24 +1,27 @@
 from django_project import models

+import factory
 import factory.django

 class ThingFactory(factory.django.DjangoModelFactory):

-    do_calculate = False
+    class Params:
+        do_calculate = False

     class Meta:
         model = models.Thing

-    class Params:
-        do_calculate = factory.Trait(
-            _bogus=factory.PostGenerationMethodCall('calculate_sum')
-        )
+    _set_sum = factory.Maybe(
+        'do_calculate',
+        factory.PostGenerationMethodCall('calculate_sum')
+    )

 class DoCalculateFactory(ThingFactory):

-    do_calculate = True
+    class Params:
+        do_calculate = True

 class Thing3Plus4Factory(DoCalculateFactory):
TauPan commented 4 years ago

I forgot to mention:

FAILED django_project/tests.py::test_thing_3_plus_4_arg - assert 1 == 3

This one indicates that not only the trait is not called, the parameters a and b are also not specialized by this:

register(
    factories.ThingFactory, 'thing3_plus4_arg', do_calculate=True, a=3, b=4)

See https://github.com/TauPan/pytest-factoryboy-reproduce-98/blob/master/django_project/conftest.py#L14