13

I started using the factory_boy package so I've set up some factories and wanted to test that the objects created don't raise any validation errors.

Here's the mixin I'm using that basically takes every factory from a module, creates an instance, then tests that there are no errors from .full_clean(). The user fixtures that are loaded are 10 instances with IDs 1 to 10.

class FactoriesTestCaseMixin:
    fixtures = [
        'user/tests/fixtures/user.json',
    ]
    module = None

    def test_factories(self):
        err_msg = '{name} caused errors:\n{errors}'
        factories = [
            (name, obj) for name, obj in inspect.getmembers(self.module, inspect.isclass)
            if obj.__module__ == self.module.__name__
            and not obj._meta.abstract
        ]
        for factory in factories:
            name = factory[0]
            instance = factory[1]()

            errors = None
            try:
                instance.full_clean()
            except ValidationError as e:
                errors = e

            self.assertTrue(errors is None, err_msg.format(name=name, errors=errors))

The mixin would be used like this

from django.test import TestCase
from order import factories

class OrderFactoriesTestCase(FactoriesTestCaseMixin, TestCase):
    module = factories

But I keep getting an IntegrityError (traceback below) after the test successfully passes in regards to fixture teardown and I can't figure out how to get around it so my tests pass with no errors.

If I run the tests for each individual app there are no errors. I never get problems in any of my other model's fixtures that also have a created_by field.

django.db.utils.IntegrityError: insert or update on table "product_product" violates foreign key constraint "product_product_created_by_id_96713f93_fk_user_user_id"
DETAIL:  Key (created_by_id)=(13) is not present in table "user_user".

What I think is happening is that a previous test is creating a new user and the factory boy Iterator is picking one of the new user IDs.. still not sure why this would cause an error after successfully passing the test.

created_by = factory.Iterator(User.objects.all())

The modules that cause this problem always have a SubFactory to the ProductFactory

product = factory.SubFactory(ProductFactory)

Any suggestions on how to solve this?

Traceback (most recent call last):
  File "/home/Development/project/venv/lib/python3.7/site-packages/django/test/testcases.py", line 274, in __call__
    self._post_teardown()
  File "/home/Development/project/venv/lib/python3.7/site-packages/django/test/testcases.py", line 1009, in _post_teardown
    self._fixture_teardown()
  File "/home/Development/project/venv/lib/python3.7/site-packages/django/test/testcases.py", line 1177, in _fixture_teardown
    connections[db_name].check_constraints()
  File "/home/Development/project/venv/lib/python3.7/site-packages/django/db/backends/postgresql/base.py", line 246, in check_constraints
    self.cursor().execute('SET CONSTRAINTS ALL IMMEDIATE')
  File "/home/Development/project/venv/lib/python3.7/site-packages/django/db/backends/utils.py", line 67, in execute
    return self._execute_with_wrappers(sql, params, many=False, executor=self._execute)
  File "/home/Development/project/venv/lib/python3.7/site-packages/django/db/backends/utils.py", line 76, in _execute_with_wrappers
    return executor(sql, params, many, context)
  File "/home/Development/project/venv/lib/python3.7/site-packages/django/db/backends/utils.py", line 84, in _execute
    return self.cursor.execute(sql, params)
  File "/home/Development/project/venv/lib/python3.7/site-packages/django/db/utils.py", line 89, in __exit__
    raise dj_exc_value.with_traceback(traceback) from exc_value
  File "/home/Development/project/venv/lib/python3.7/site-packages/django/db/backends/utils.py", line 82, in _execute
    return self.cursor.execute(sql)
django.db.utils.IntegrityError: insert or update on table "product_product" violates foreign key constraint "product_product_created_by_id_96713f93_fk_user_user_id"
DETAIL:  Key (created_by_id)=(12) is not present in table "user_user".
2
  • Hi! Did you ever find a solution to this?
    – tjespe
    Commented Jan 26, 2021 at 10:59
  • @tjespe I have not
    – bdoubleu
    Commented Jan 26, 2021 at 11:03

2 Answers 2

1

I have a related problem which I've fixed by specializing _fixture_teardown in my test case class.

_fixture_teardown is implemented in django.test.testcases to call the django command 'flush' which attempts to remove all data from the database. I don't know if this is useful for you or not. In my scenario where I persist the test database and use --keepdb, it was causing problems.

As I don't need (or want) to flush the test database, I've simply specialized the method to do nothing. This solves my problem and might help solve yours.

0

I have found the cause of the problem in very similar case. I was creating model with model_bakery. And I have this method in my ModelAdmin:

def formfield_for_foreignkey(self, db_field, request, **kwargs):
    if db_field.name == "author":
        db_field.default = request.user
        return super(PostAdmin, self).formfield_for_foreignkey(
            db_field, request, **kwargs
        )

First executed the test that was rendering of the ModelAdmin listedit view, which called this function and set the default request.user to the Post.author field.

In the next test (after request.user was already dropped from DB) I created new Post with:

baker.make("Post", title="Foo post")

Which assigned the ID of inexistent default user to the author field.

Then it throws an exception during _fixture_teardown or just simple print(post.author).

Not the answer you're looking for? Browse other questions tagged or ask your own question.