Pytest-django: pytest غير قادر على اكتشاف خطأ سلامة django

تم إنشاؤها على ٦ أغسطس ٢٠١٩  ·  8تعليقات  ·  مصدر: pytest-dev/pytest-django

pytest غير قادر على اكتشاف خطأ سلامة django

# models.py
class A(models.Model):
    val = models.BooleanField(default=True)

class B(models.Model):
    a = models.ForeignKey(A, on_delete=models.CASCADE)
# tests.py
from django.db import IntegrityError
import pytest

@pytest.mark.django_db
def test_integrity():
    with pytest.raises(IntegrityError):
        # using object A with id=1 before creating it 
        B.objects.create(a_id=1)

بعد تشغيل pytest سيظهر خطأين للوظيفة test_integrity :

  • الإخفاقات: "لم ترفع"
  • خطأ في teardown: "django.db.utils.IntegrityErity: الإدراج أو التحديث في الجدول" core_b "ينتهك قيد المفتاح الخارجي" core_b_a_id_2a5b83ce_fk_core_a_id "

وهو في الأساس: "هنا خطأ في عدم رفع IntegrityError في test_foo ، وهنا خطأ لرفع IntegrityError في test_foo ، تعامل معها"

يمكنني اكتشاف خطأ النزاهة بدون مشاكل خارج pytest:

root<strong i="19">@1f28fc583eed</strong>:/app# ./manage.py shell
Python 3.6.7 (default, Oct 24 2018, 22:47:56)
[GCC 6.3.0 20170516] on linux
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from core.tests import test_integrity
>>> test_integrity()
>>> exit()

قائمة النقاط

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

الإصدارات:

pytest: 4.5.0
django: 2.2.4
  • [x] وصف تفصيلي للخطأ أو الاقتراح
  • [x] ناتج pip list من البيئة الافتراضية التي تستخدمها
  • [x] إصدارات pytest ونظام التشغيل
  • [x] مثال صغير إن أمكن

التعليق الأكثر فائدة

تضمين التغريدة
راجع للشغل: يُفضل عادةً لصق التعليمات البرمجية على لقطات الشاشة.

أعتقد أنك بحاجة إلى استخدام @pytest.mark.django_db(transaction=True) ثم ربما. وإلا فلا يزال لديك المعاملة الذرية الخارجية.
يمكنك أيضًا محاولة إغلاق ذلك من قبل بدلاً من ذلك (نظرًا لأن اختبارات المعاملات بطيئة بشكل عام).

ال 8 كومينتر

كيف يبدو تتبع المكدس لـ django.db.utils.IntegrityError ؟
يرجى أيضًا تجربته مع أحدث إصدار من pytest و pytest-django.

لقد قمت بالفعل بحذف مثال لمشروع ، ولكن فيما يلي سجلات أخطاء من مشروع آخر حيث أختبر نفس الشيء بشكل أساسي: كائن تم إنشاؤه باستخدام معرف كائن ذي صلة غير موجود حاليًا.

النزاهة سجلات الخطأ

______________________________ ERROR at teardown of TestReactionModel.test_safe_create_without_user ______________________________

self = <django.db.backends.utils.CursorWrapper object at 0x7f7c8e278898>, sql = 'SET CONSTRAINTS ALL IMMEDIATE', params = None
ignored_wrapper_args = (False, {'connection': <django.db.backends.postgresql.base.DatabaseWrapper object at 0x7f7c95001898>, 'cursor': <django.db.backends.utils.CursorWrapper object at 0x7f7c8e278898>})

    def _execute(self, sql, params, *ignored_wrapper_args):
        self.db.validate_no_broken_transaction()
        with self.db.wrap_database_errors:
            if params is None:
>               return self.cursor.execute(sql)
E               psycopg2.errors.ForeignKeyViolation: insert or update on table "core_reaction" violates foreign key constraint "core_reaction_user_id_968339ea_fk_core_user_id"
E               DETAIL:  Key (user_id)=(111) is not present in table "core_user".

/usr/local/lib/python3.6/site-packages/django/db/backends/utils.py:82: ForeignKeyViolation

The above exception was the direct cause of the following exception:

self = <django.test.testcases.TestCase testMethod=__init__>

    def _post_teardown(self):
        """
        Perform post-test things:
        * Flush the contents of the database to leave a clean slate. If the
          class has an 'available_apps' attribute, don't fire post_migrate.
        * Force-close the connection so the next test gets a clean cursor.
        """
        try:
>           self._fixture_teardown()

/usr/local/lib/python3.6/site-packages/django/test/testcases.py:1009:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
/usr/local/lib/python3.6/site-packages/django/test/testcases.py:1177: in _fixture_teardown
    connections[db_name].check_constraints()
/usr/local/lib/python3.6/site-packages/django/db/backends/postgresql/base.py:246: in check_constraints
    self.cursor().execute('SET CONSTRAINTS ALL IMMEDIATE')
/usr/local/lib/python3.6/site-packages/django/db/backends/utils.py:67: in execute
    return self._execute_with_wrappers(sql, params, many=False, executor=self._execute)
/usr/local/lib/python3.6/site-packages/django/db/backends/utils.py:76: in _execute_with_wrappers
    return executor(sql, params, many, context)
/usr/local/lib/python3.6/site-packages/django/db/backends/utils.py:84: in _execute
    return self.cursor.execute(sql, params)
/usr/local/lib/python3.6/site-packages/django/db/utils.py:89: in __exit__
    raise dj_exc_value.with_traceback(traceback) from exc_value
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <django.db.backends.utils.CursorWrapper object at 0x7f7c8e278898>, sql = 'SET CONSTRAINTS ALL IMMEDIATE', params = None
ignored_wrapper_args = (False, {'connection': <django.db.backends.postgresql.base.DatabaseWrapper object at 0x7f7c95001898>, 'cursor': <django.db.backends.utils.CursorWrapper object at 0x7f7c8e278898>})

    def _execute(self, sql, params, *ignored_wrapper_args):
        self.db.validate_no_broken_transaction()
        with self.db.wrap_database_errors:
            if params is None:
>               return self.cursor.execute(sql)
E               django.db.utils.IntegrityError: insert or update on table "core_reaction" violates foreign key constraint "core_reaction_user_id_968339ea_fk_core_user_id"
E               DETAIL:  Key (user_id)=(111) is not present in table "core_user".

/usr/local/lib/python3.6/site-packages/django/db/backends/utils.py:82: IntegrityError

عصابة بيتيست المحدثة:
Screen Shot 2019-08-07 at 03 06 50

لكن الخطأ لا يزال قائما.
أيضا ، هناك شيء آخر نسيت أن أذكره.

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

سلوك متوقع

إذا لم يكن هناك كائن A -> يتم طرح IntegrityError -> تعامل معه (على سبيل المثال ، قم بإنشاء كائن A بمعرف = 1 وأعد تشغيله).

الواقع

تم إنشاء الكائن B على أي حال على الرغم من وجود قيد مفتاح خارجي ، يتم طرح IntegrityError (Key (a_id) = (1) غير موجود في الجدول "app_a") ولكن لم يتم جلبه بواسطة try / except. كل شيء عابث.

هل حاولت:

# 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-بشكل صريح
لاحظ بشكل خاص التحذير:

تجنب اصطياد الاستثناءات داخل atomic

أعتقد أنك ترى هذا لأنه يتم افتراضيًا تشغيل اختبارات @ pytest.mark.django_db في كتلة ذرية ضمنية

لقد حاولت التفاف الكود إلى @transaction.atomic() كما في المثال الخاص بك ، لكنه لم ينجح.

Screen Shot 2019-08-07 at 15 27 39

الأخطاء هي نفسها.

تضمين التغريدة
راجع للشغل: يُفضل عادةً لصق التعليمات البرمجية على لقطات الشاشة.

أعتقد أنك بحاجة إلى استخدام @pytest.mark.django_db(transaction=True) ثم ربما. وإلا فلا يزال لديك المعاملة الذرية الخارجية.
يمكنك أيضًا محاولة إغلاق ذلك من قبل بدلاً من ذلك (نظرًا لأن اختبارات المعاملات بطيئة بشكل عام).

أصلح كل شيء @pytest.mark.django_db(transaction=True) . لم أكن بحاجة حتى إلى إضافة @trasaction.atomic داخلي. شكرا لك.

كمرجع ، يمكن اختبار ذلك بدون transaction=True (وهو أمر بطيء):

@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 على هذا بنفسه بـ _fixture_teardown (لجميع اتصالات DB): https://github.com/django/django/blob/2c66f340bb50ed6790d839157dff64456b497a43/django/test/testcases.py#L1171 -L1177

هل كانت هذه الصفحة مفيدة؟
0 / 5 - 0 التقييمات