Pytest-django: النطاقات غير المتوافقة لـ live_server و المعاملات_db

تم إنشاؤها على ٢٨ يناير ٢٠١٧  ·  8تعليقات  ·  مصدر: pytest-dev/pytest-django

إن تركيبات live_server محددة بالجلسة بينما يتم تحديد نطاق الوظيفة transactional_db (الذي يمكّنه live_server ضمنيًا).

نتيجة لذلك ، يمكن أن يتعامل live_server مع الطلبات خلال كامل مدة مجموعة الاختبار. يمكن أن تتعارض هذه الطلبات مع عمليات تدفق قاعدة البيانات التي يؤديها transactional_db بعد كل اختبار أو مع الفترات التي يتم فيها تعطيل الوصول إلى قاعدة البيانات بين اختبارين.

إليك ما يبدو عليه تسلسل الأحداث:

  • الجلسة: live_server بدأت المباراة بـ LiveServerThread

    • الوظيفة: transactional_db تشغيلات التثبيت (يتم تشغيلها بواسطة الوظيفة ذات النطاق ، autouse _live_server_helper fixture) وتضيف TransactionTestCase._post_teardown إلى Finalizers

    • الوظيفة: يقدم الاختبار طلبًا إلى الخادم المباشر ، ربما من متصفح يحركه السيلينيوم ، وينتهي قبل أن يتمكن الخادم المباشر من الاستجابة لهذا الطلب (يمكن أن يحدث هذا لأسباب مختلفة ؛ أحد الأسباب الشائعة هو الوصول إلى صفحة تقدم طلبات AJAX عندما يتم تحميله في نهاية الاختبار والخروج من وظيفة الاختبار قبل اكتمال جميع هذه الطلبات)

    • الوظيفة: يتم تشغيل finalizer transactional_db ومحاولات مسح قاعدة البيانات ، بما يتعارض مع الطلبات التي لا يزال الخادم المباشر يعالجها

  • الجلسة: live_server finalizer ينتظر LiveServerThread للإنهاء

يمكن أن يتسبب التعارض في حالات توقف تام بين أي استعلام SQL من طلب HTTP يتم التعامل معه بواسطة الخادم المباشر والاستعلام الذي يمسح قاعدة البيانات ، والذي يبدو كالتالي:

Exception Database test_xxxxxxxx couldn't be flushed. Possible reasons:
  * The database isn't running or isn't configured correctly.
  * At least one of the expected database tables doesn't exist.
  * The SQL was invalid.
Hint: Look at the output of 'django-admin sqlflush'. That's the SQL this command wasn't able to run.
The full error: deadlock detected
DETAIL:  Process 37253 waits for AccessExclusiveLock on relation 131803 of database 131722; blocked by process 37250.
Process 37250 waits for AccessShareLock on relation 132659 of database 131722; blocked by process 37253.
HINT:  See server log for query details.

يعد وضع الفشل هذا مزعجًا بشكل خاص لأن قاعدة البيانات لا يتم مسحها ، مما يتطلب التشغيل التجريبي التالي لإعادة إنشاء قاعدة البيانات وبالتالي إبطال فوائد --reuse-db .

إذا لم يتم تمكين إعادة استخدام قاعدة البيانات ، فقد يفشل إتلاف قاعدة بيانات الاختبار مع:

django.db.utils.OperationalError: database "test_xxxxxxxx" is being accessed by other users
DETAIL:  There is 1 other session using the database.

لقد رأيت أيضًا استعلامات SQL من طلبات HTTP التي يعالجها الخادم المباشر والتي تفشل مع:

Failed: Database access not allowed, use the "django_db" mark, or the "db" or "transactional_db" fixtures to enable it.

لأن أداة Finalizer transactional_db قامت بمسح قاعدة البيانات وحظر الوصول حتى الاختبار التالي.

أخطط للبحث عن حلول وسأحدث هذه التذكرة بالنتائج التي توصلت إليها.

bug

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

لقد حققت بعض النجاح مع الكود أدناه ، ولكن (YMMV)

@pytest.fixture(name='live_server', scope='function')
def _live_server(live_server):
    # This rescopes the live_server fixture to function scope
    # See https://github.com/pytest-dev/pytest-django/issues/454
    return live_server

scope='function' غير مطلوب تقنيًا لأن النطاق الافتراضي هو function ولكن تمت إضافته للتوضيح

ال 8 كومينتر

يجري في هذا أيضا. هل وجدت أي شيء؟

لا.

سأبلغ نتائجي هنا إذا وجدت وقتًا للعمل على حل. إنه ليس بالقرب من أعلى قائمة TODO الخاصة بي.

أواجه نفس المشكلة (؟) ، لا يرى live_server fixture التغييرات التي تم إجراؤها على قاعدة البيانات ، والتي تعمل في معاملتها الخاصة. aaugustin ، هل يبدو ذلك محتملًا بالنسبة لك؟

كنت أتساءل ... ربما يجب أن أتخلى عن تركيبات الخادم المباشر تمامًا وأكتب بديلي. سيتكون من تشغيل "manager.py runserver" في موضوع. باستثناء توجيهه إلى قاعدة بيانات اختبارية ، لن أقوم بتحديث إعدادات المشروع. في مرحلة ما قد أحتاج ذلك. أيضًا ، لا يدعم LiveServerTestCase STATICFILES_FINDERS المخصص باستثناء نظام الملفات الأول ...

أنا أيضًا أواجه هذه المشكلة. الحل الحالي الذي أستخدمه هو تجاوز تركيبات live_server بحيث يتم تحديد نطاق الوظيفة. على وجه التحديد ، يحتوي ملف conftest.py على ما يلي:

from pytest_django.fixtures import live_server as orig_live_server

@pytest.fixture(scope='function')
def live_server(request):
    """
        Workaround inspired by https://github.com/mozilla/addons-server/pull/4875/files#diff-0223c02758be2ac7967ea22c6fa4b361R96
    """
    return orig_live_server(request)

يبدو أن هذا يعمل ، على الرغم من أنه من المسلم به أنه يبطئ جميع حالات الاختبار التي تستخدمه _way_ إلى أسفل. إذا رأى أي شخص مشكلة في هذا النهج ، فيرجى إبلاغي بذلك.

لدي مشكلة مماثلة: live_server لا يعمل ، إنه يطبع الخطأ no such table: django_session .

لقد أصلح حل

RemovedInPytest4Warning: يسمى Fixture "live_server" مباشرة. لا يُقصد باستدعاء التركيبات مباشرة ، بل يتم إنشاؤها تلقائيًا عندما تطلبها وظائف الاختبار كمعلمات.

هل هناك أي طريقة أخرى لاستخدام live_server مع قاعدة بيانات؟

يثير حل Helumpago خطأً بدلاً من التحذير منذ pytest 4.0 (2018-11-13) :(

محدث: يبدو أنه يعمل (لكن هذا قبيح)

from pytest_django.fixtures import live_server as orig_live_server
live_server = pytest.fixture(scope='function')(orig_live_server.__wrapped__)

لقد حققت بعض النجاح مع الكود أدناه ، ولكن (YMMV)

@pytest.fixture(name='live_server', scope='function')
def _live_server(live_server):
    # This rescopes the live_server fixture to function scope
    # See https://github.com/pytest-dev/pytest-django/issues/454
    return live_server

scope='function' غير مطلوب تقنيًا لأن النطاق الافتراضي هو function ولكن تمت إضافته للتوضيح

أنا أواجه هذا أيضًا. لا شك أن جميع المتغيرات المذكورة أعلاه من الإصلاح ستعمل.

لكني أتساءل: ما هو السلوك الصحيح هنا؟ يبدو مثل جذور المشكلة هي في الواقع تلك الطلبات AJAX يزال يحدث بعد انتهاء الاختبار. بحق ، يجب عليهم منع قاعدة البيانات من التنظيف.

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

يمكنني التفكير في بعض الخيارات:

  1. اطلب من عميل السيلينيوم الخاص بك الانتظار حتى تنتهي جميع طلبات AJAX المعلقة قبل التنازل عن السيطرة. أقوم بإغلاق استخدام السيلينيوم الخاص بي ، لذا يكون هذا واضحًا إلى حد ما ، ولدى تطبيقي طريقة للإشارة إلى انتهاء التحميل. لكن هذا غير عملي بالنسبة للكثيرين. خاصة بالنسبة للتطبيقات التي "تستطلع" في الخلفية. قد يكون هناك دائمًا طلب معلق.
  2. بطريقة ما أخرج أوامر pytest / flush لقتل جميع العمليات المعلقة في قاعدة البيانات. شيء من هذا القبيل psql postgres -c 'SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE pid <> EVERYTHING_BUT_PYTEST_PID';
  3. تغيير النطاق ، ولكن كما أشرنا ، فإن هذا يبطئ كل شيء.
  4. ربما هناك طريقة سحرية يقوم بها السيلينيوم في المرتبة الأولى بطريقة أكثر تنظيماً وعالمية. شيء من هذا القبيل: انتظر حتى تنتهي جميع طلبات AJAX المعلقة ولكن لا تبدأ بأي طلبات جديدة. لست على دراية كافية بالسيلينيوم للتحقق من ذلك الآن.
هل كانت هذه الصفحة مفيدة؟
0 / 5 - 0 التقييمات