pytest-factoryboy
pytest-factoryboy copied to clipboard
LazyFixture ignores Subfactory attribute assignment
I am using pytest-factoryboy to test some of my django Code.
When using A model 'MainModel' with a ForeignKey to another Model 'OneOnly' I create a Factory for both like the following.
models.py:
from django.db import models
class OnlyOne(models.Model):
name = models.CharField(max_length=16)
class MainModel(models.Model):
name = models.CharField(max_length=16)
one = models.ForeignKey(OnlyOne)
factories.py
import factory
import factory.fuzzy
from bug.models import MainModel
from bug.models import OnlyOne
class OnlyOneFactory(factory.DjangoModelFactory):
class Meta(object):
model = OnlyOne
name = factory.Sequence(lambda n: 'OnlyOne {}'.format(n))
class MainModelFactory(factory.DjangoModelFactory):
class Meta(object):
model = MainModel
name = factory.Sequence(lambda n: 'MainModel {}'.format(n))
one = factory.SubFactory(OnlyOneFactory, name='Initial Name')
So I want the Subfactory to set OnlyOnes' name always to Initial Name. As of the documentation this is how to do it (see example here ):
one = factory.SubFactory(OnlyOneFactory, name='Initial Name')
Now I have written some basic tests to test that the name of OnlyOne is set correctly. And it is if I use the factory build and/or create method, but however not if I use the LazyFixture.
test_factories.py:
"""Test the factories."""
import pytest
@pytest.mark.django_db
def test_main_model_factory__create(main_model_factory):
"""Test if the MainModelFactory.create() works properly."""
main_model = main_model_factory.create()
assert main_model.one.name == 'Initial Name'
@pytest.mark.django_db
def test_main_model_factory__build(main_model_factory):
"""Test if the MainModelFactory.build() works properly."""
main_model = main_model_factory.build()
assert main_model.one.name == 'Initial Name'
@pytest.mark.django_db
def test_main_model_fixture(main_model):
"""Test if the MainModelFactory shortcut works properly."""
assert main_model.one.name == 'Initial Name'
Test output is the following:
pytest
=========================================================== test session starts ============================================================
platform linux2 -- Python 2.7.12+, pytest-3.0.3, py-1.4.31, pluggy-0.4.0
django settings: factoryboy_subfactory_attribute_bug.settings (from ini file)
rootdir: /home/daniel/workspace/private/factoryboy_subfactory_attribute_bug/factoryboy_subfactory_attribute_bug, inifile: pytest.ini
plugins: bdd-2.18.0, factoryboy-1.1.6, django-2.9.1, timeout-1.0.0, blockage-0.2.0, cov-2.4.0
collected 3 items
bug/tests/test_factories.py ..F
================================================================= FAILURES =================================================================
_________________________________________________________ test_main_model_fixture __________________________________________________________
main_model = <MainModel: MainModel object>
@pytest.mark.django_db
def test_main_model_fixture(main_model):
"""Test if the MainModelFactory shortcut works properly."""
> assert main_model.one.name == 'Initial Name'
E assert 'OnlyOne 2' == 'Initial Name'
E - OnlyOne 2
E + Initial Name
bug/tests/test_factories.py:22: AssertionError
==================================================== 1 failed, 2 passed in 0.32 seconds ====================================================
The question now is should it set the name as expected or am I misunderstanding the functionality of the LazyFixture used?
I am attaching a sample.tar.gz with a working django app to simulate this. sample.tar.gz
- Unpack and cd to folder
- virtualenv env -p python3
- pip install django python-factoryboy pytest
- run pytest
Thanks for the help.
Edit: If you need any further information please feel free to ask. Sorry if something is missing I am new to submitting Issues on an open source project.
For now a workaround is to overwrite the _create method of my factory so it sets the attribute like:
@classmethod
def _create(cls, model_class, *args, **kwargs):
obj = super(ArticleFactory, cls)._create(model_class, *args, **kwargs)
obj.one.name = 'Initial Name'
obj.one.save()
return obj
But another database access with the save operation is basically not what we want, is it?
+1
_create method override is so bothersome things