Closed vanyakosmos closed 5 years ago
How does the stacktrace for django.db.utils.IntegrityError
look like?
Please also try it with the latest pytest and pytest-django.
I already deleted example project, but here are error logs from another project where I test essentially the same thing: object created with id of related object that doesn't currently exist.
Updated pytest gang:
But the error is still present. Also, there is another thing that I forgot to mention.
class A(Model):
pass
class B(Model):
a = ForeignKey(A, on_delete=CASCADE)
assert A.objects.all().count() == 0 # ok
try:
b = B.object.create(a_id=1)
except IntegrityError:
# will go here only if I run this code outside of pytest
print('error')
b = None
# ... another logic to deal with situation
assert b is None # will fail in pytest but will work if I run this code normally
assert b.a_id == 1 # will work in pytest
assert A.objects.count() == 0 # will work in pytest
If there is no object A -> IntegrityError is thrown -> deal with it (eg create object A with id=1 and rerun).
Object B is created anyway despite having a foreign key constraint, IntegrityError (Key (a_id)=(1) is not present in table "app_a") is thrown but is not fetched by try/except. Everything is messed up.
have you tried:
# tests.py
from django.db import transaction, IntegrityError
import pytest
@pytest.mark.django_db
def test_integrity():
with pytest.raises(IntegrityError):
with transaction.atomic():
B.objects.create(a_id=1)
https://docs.djangoproject.com/en/2.2/topics/db/transactions/#controlling-transactions-explicitly Note particularly the admonition:
Avoid catching exceptions inside
atomic
I believe you're seeing this because by default @pytest.mark.django_db tests run in an implicit atomic block
I've tried to wrap code into @transaction.atomic()
like in your example, but it didn't work.
Errors are the same.
@vanyakosmos btw: pasting code is usually preferred to screenshots.
I think you need to use @pytest.mark.django_db(transaction=True)
then probably. Otherwise you still have the outer atomic transaction.
You could also try to close that one before instead (since transactional tests are slow in general).
@pytest.mark.django_db(transaction=True)
fixed everything. I didn't even need to add inner @trasaction.atomic
. Thank you.
For reference, this can be tested without transaction=True
(which is slow):
@pytest.mark.django_db
def test_request_integrity():
from django.db import IntegrityError
from django.db import connection
B.objects.create(a_id=1)
with pytest.raises(IntegrityError) as excinfo:
connection.check_constraints()
assert 'Key (a_id)=(1) is not present in table "app_a"' in str(excinfo.value)
Django calls this itself in _fixture_teardown
(for all DB connections): https://github.com/django/django/blob/2c66f340bb50ed6790d839157dff64456b497a43/django/test/testcases.py#L1171-L1177
connection.check_constraints()
I can't find any documentation about this? Is it a public-facing feature?
pytest is unable to catch django's IntegrityError
After running
pytest
it will show two errors fortest_integrity
function:Which is basically: "here is an error about not raising IntegrityError in test_foo, and here is an error for raising IntegrityError in test_foo, deal with it"
I can catch Integrity error w/o problems outside of pytest:
pip list
``` Package Version ------------------- --------- apipkg 1.5 asn1crypto 0.24.0 atomicwrites 1.3.0 attrs 19.1.0 certifi 2019.3.9 cffi 1.12.3 chardet 3.0.4 codecov 2.0.15 coverage 4.5.4 cryptography 2.6.1 dj-database-url 0.5.0 Django 2.2.3 django-dbbackup 3.2.0 django-extensions 2.1.7 emoji 0.5.2 execnet 1.6.1 future 0.17.1 gunicorn 19.9.0 idna 2.8 more-itertools 7.0.0 pip 18.1 pluggy 0.11.0 psycopg2-binary 2.8.2 py 1.8.0 pycparser 2.19 pytest 4.5.0 pytest-cov 2.7.1 pytest-django 3.4.8 pytest-forked 1.0.2 pytest-mock 1.10.4 pytest-xdist 1.29.0 python-telegram-bot 12.0.0b1 pytz 2019.1 redis 3.2.1 regex 2019.4.14 requests 2.22.0 schedule 0.6.0 setuptools 40.4.3 six 1.12.0 sqlparse 0.3.0 tornado 6.0.2 urllib3 1.25.3 wcwidth 0.1.7 wheel 0.32.2 whitenoise 4.1.3 ```
Versions:
pip list
from the virtual environment you are using