Pytest-django: دعم التنظيف بين الاختبارات بقواعد بيانات متعددة

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

لا يقوم pytest-django بالتنظيف بين الاختبارات عند استخدام Django مع قواعد بيانات متعددة. تتعلق المشكلة بسؤال StackOverflow هذا: http://stackoverflow.com/questions/10121485/django-testcase-not-using-transactions-on-secondary-database

تستخدم أداة db TestCase من Django تحت الأغطية.

هذا هو الرمز من المباراة الأصلية db :

case = TestCase(methodName='__init__')
case._pre_setup()
request.addfinalizer(case._post_teardown)
request.addfinalizer(_django_cursor_wrapper.disable)

إليك كيفية تصحيحه كحل بديل في التعليمات البرمجية الخاصة بي:

case = TestCase(methodName='__init__')
case.multi_db = True
case._pre_setup()
request.addfinalizer(case._post_teardown)
request.addfinalizer(_django_cursor_wrapper.disable)

من الواضح أن علامة multi_db موجودة ويتم تعيينها افتراضيًا على False لسبب ما. لست متأكدًا من الطريقة الصحيحة لدمج دعم multi_db في pytest_django ، ولست متأكدًا من الطريقة الصحيحة لاختبار مثل هذا التغيير.

إذا كان لديك اقتراح يمكنني العمل على طلب سحب.

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

خيار آخر لتمكين multi_db لمن يبحثون عن حل مؤقت. ضع هذا في conftest.py

def pytest_sessionstart(session):
    from django.test import TransactionTestCase
    TransactionTestCase.multi_db = True

ال 20 كومينتر

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

DATABASES = {
    'default': {
        'ENGINE'   : 'django.db.backends.sqlite3',
        'NAME'     : 'default.db',
    },
    'operations': {
        'ENGINE'   : 'django.db.backends.sqlite3',
        'NAME'     : 'operations.db',
    },
}

ترتبط معظم / كل نماذجي بـ "عمليات" قاعدة البيانات الثانية. أعتقد أن TestCase من Django سوف يقوم بمسح المعاملات بشكل صحيح على ديسيبل الافتراضي ، ولكن ليس على الآخرين ما لم يكن multi_db=True .

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

from collection.models import Foo

def test_one(db):
    Foo.objects.create()
    assert Foo.objects.count() == 1

def test_two(db):
    assert Foo.objects.count() == 0

هذا ناتج من خلال تركيباتي المصححة db الموضحة أعلاه:

$ py.test multi_db_test/ -vv
======================================= test session starts =======================================
platform linux2 -- Python 2.7.2 -- py-1.4.20 -- pytest-2.5.2 -- bin/python
plugins: django
collected 2 items

multi_db_test/test_foo.py:3: test_one PASSED
multi_db_test/test_foo.py:7: test_two PASSED

==================================== 2 passed in 5.75 seconds =====================================

هذا هو الناتج مع تركيبات pytest_django الخاصة بـ db :

$ py.test multi_db_test/ -vv
======================================= test session starts =======================================
platform linux2 -- Python 2.7.2 -- py-1.4.20 -- pytest-2.5.2 -- bin/python
plugins: django
collected 2 items

multi_db_test/test_foo.py:3: test_one PASSED
multi_db_test/test_foo.py:7: test_two FAILED

============================================ FAILURES =============================================
____________________________________________ test_two _____________________________________________

db = None

    def test_two(db):
>       assert Foo.objects.count() == 0
E       assert 1 == 0
E        +  where 1 = <bound method Manager.count of <django.db.models.manager.Manager object at 0x3cd0890>>()
E        +    where <bound method Manager.count of <django.db.models.manager.Manager object at 0x3cd0890>> = <django.db.models.manager.Manager object at 0x3cd0890>.count
E        +      where <django.db.models.manager.Manager object at 0x3cd0890> = Foo.objects

multi_db_test/test_foo.py:8: AssertionError
=============================== 1 failed, 1 passed in 5.85 seconds ===============================

لذا من النظرة السريعة يبدو أن أداة multi_db و pytest.mark.django_db(multi=True) يمكن أن تعمل كواجهة برمجة تطبيقات؟ أي شخص آخر لديه أي أفكار أفضل؟

هذا يبدو منطقيا. بالتأكيد ليس لدي أفكار أخرى ، أفضل أو غير ذلك.

مرحبا. هل هناك أي نقاش حول دعم multi_db بجانب هذه المشكلة؟ يقول المستندات أنه يجب على المرء أن يتواصل ولكن ما هو الوضع الحالي في هذا الشأن؟

slafs لم يكن هناك الكثير من المناقشات / الطلبات لدعم قواعد البيانات المتعددة بصرف النظر عن هذا.

سيكون من السهل جدًا تمرير multi_db=True ، لكننا نحتاج أيضًا إلى نوع من الاختبارات لذلك ، لذلك نحتاج أيضًا إلى إعداد قاعدة بيانات ثانوية. إذا كان أي شخص على استعداد للعمل على هذا ، فسيكون رائعًا :)

لقد كنت أستخدم الحل البديل منftobia في

كان هناك تغيير في Django لجعل إعداد معالجة الكود / tearDown يبحث عن سمة فئة multi_db بدلاً من سمة مثيل.

نجح تغيير ftobia إلى ما يلي:

case = TestCase(methodName='__init__')
case.__class__.multi_db = True
case._pre_setup()
request.addfinalizer(case._post_teardown)
request.addfinalizer(_django_cursor_wrapper.disable)

+1: +1:

هذا يمنعنا من التحول من عداء اختبار Django إلى pytest.

لماذا لا تستخدم django_case.multi_db = True هنا https://github.com/pytest-dev/pytest-django/blob/master/pytest_django/fixture.py#L107 افتراضيًا؟

+1

+1

أعتقد أن وجود علامة على علامة django_db مثل I good API:

@pytest.mark.django_db(transactional=True, multi_db=True)
def test_a():
    pass

يجب أن يكون التطبيق مشابهًا نسبيًا لـ reset_sequences (PR # 308) أو serialized_rollback (PR # 353).

يتمثل الجزء "الصعب" في تحسين مجموعة الاختبارات الداخلية لـ pytest-django لاحتواء قواعد بيانات متعددة.

إذا أراد شخص ما العمل في هذا ، فسأقوم بالتأكيد بمراجعته والمساعدة في دمجه. لا تتردد في بدء العمل على العلاقات العامة إذا كان هذا أمرًا مثيرًا للاهتمام بالنسبة لك!

ترك بعض التعليقات على https://github.com/pytest-dev/pytest-django/pull/397#issuecomment -261987751 ، وأنشأ علاقات عامة تسمح بتغيير هذا بطريقة عامة: https: // github. كوم / pytest-dev / pytest-django / pull / 431.

خيار آخر لتمكين multi_db لمن يبحثون عن حل مؤقت. ضع هذا في conftest.py

def pytest_sessionstart(session):
    from django.test import TransactionTestCase
    TransactionTestCase.multi_db = True

لأي شخص آخر يبحث عن حلول بديلة ، لاحظ أنه تم إهمال multi_db في Django 2.2. البديل الذي نجح معي هو:

TransactionTestCase.databases = set(settings.DATABASES.keys())

jcushman ، هل تمانع في مشاركة

لأي سبب كان ، pytest_sessionstart() في قاعدتنا conftest.py لم يفعل شيئًا. أخذت صفحة من # 342 جنبًا إلى جنب مع الأشياء القياسية pytest monkeypatch وحصلت على ما يلي لاستخدام قاعدة البيانات المتعددة الخاصة بنا:

# OPTION 1: Function fixture; must be included with tests
@pytest.fixture
def django_db_multiple(monkeypatch, request, settings):
    """
    Ensure all test functions using Django test cases have multiple database
    support. This is mostly/only so that Django will wrap ALL database use with
    atomic blocks like it does for DEFAULT_DB_ALIAS.

    https://github.com/django/django/blob/master/django/test/testcases.py#L903
    https://github.com/pytest-dev/pytest-django/issues/76
    """
    from django.test import TestCase
    from django.test import TransactionTestCase

    db_keys = set(settings.DATABASES.keys())

    monkeypatch.setattr(TestCase, 'databases', db_keys)
    monkeypatch.setattr(TransactionTestCase, 'databases', db_keys)

@pytest.mark.django_db
def test_some_test(django_db_multiple):
    pass

# OPTION 2: Session fixture
@pytest.fixture(autouse=True, scope='session')
def django_db_multiple():
    """
    Ensure all test functions using Django test cases have multiple database
    support. This is mostly/only so that Django will wrap ALL database use with
    atomic blocks like it does for DEFAULT_DB_ALIAS.

    https://github.com/django/django/blob/master/django/test/testcases.py#L903
    https://github.com/pytest-dev/pytest-django/issues/76
    https://github.com/pytest-dev/pytest/issues/1872
    """
    from _pytest.monkeypatch import MonkeyPatch
    from django.test import TestCase
    from django.test import TransactionTestCase
    from django.conf import settings

    db_keys = set(settings.DATABASES.keys())

    monkeypatch = MonkeyPatch()
    monkeypatch.setattr(TestCase, 'databases', db_keys)
    monkeypatch.setattr(TransactionTestCase, 'databases', db_keys)

    yield monkeypatch

    monkeypatch.undo()

_تعديل: انتقلنا إلى جلسة ثابتة.

من أجل عدم فقدان القوى الخارقة من خلال التبديل إلى حالات اختبار Django غير الملائمة ، قمت بنسخ وتصحيح الأجزاء الداخلية لـ pytest-django لهذا الاختراق القبيح (الذي يعمل!):

from typing import Optional, Type, TypeVar

import pytest
from django.test import TransactionTestCase
from pytest_django.django_compat import is_django_unittest

TestCase = TypeVar('TestCase', bound=TransactionTestCase)


def _django_db_fixture_helper(
    request, django_db_blocker, transactional: bool = False, reset_sequences: bool = False,
) -> Optional[Type[TestCase]]:
    if is_django_unittest(request):
        return None

    if not transactional and 'live_server' in request.fixturenames:
        # Do nothing, we get called with transactional=True, too.
        return None

    django_db_blocker.unblock()
    request.addfinalizer(django_db_blocker.restore)

    if transactional:
        from django.test import TransactionTestCase as DjangoTestCase  # noqa: WPS433

        if reset_sequences:

            class ResetSequenceTestCase(DjangoTestCase):  # noqa: WPS431
                reset_sequences = True

            DjangoTestCase = ResetSequenceTestCase  # type: ignore[misc] # noqa: N806
    else:
        from django.test import TestCase as DjangoTestCase  # type: ignore[no-redef] # noqa: WPS433

    return DjangoTestCase  # type: ignore[return-value]


@pytest.fixture()
def db_case(request, django_db_setup, django_db_blocker):
    """Require a django test database.

    This database will be setup with the default fixtures and will have
    the transaction management disabled. At the end of the test the outer
    transaction that wraps the test itself will be rolled back to undo any
    changes to the database (in case the backend supports transactions).
    This is more limited than the ``transactional_db`` resource but
    faster.

    If multiple database fixtures are requested, they take precedence
    over each other in the following order (the last one wins): ``db``,
    ``transactional_db``, ``django_db_reset_sequences``.
    """
    if 'django_db_reset_sequences' in request.fixturenames:
        request.getfixturevalue('django_db_reset_sequences')
    if (
        'transactional_db' in request.fixturenames
        or 'live_server' in request.fixturenames
    ):
        request.getfixturevalue('transactional_db')
    else:
        django_case: Optional[Type[TransactionTestCase]] = _django_db_fixture_helper(
            request, django_db_blocker, transactional=False,
        )

        def factory(dbs=None):  # noqa: WPS430
            if django_case is None:
                return
            CaseType: Type[TransactionTestCase] = django_case  # noqa: N806
            if dbs is not None:
                class DatabasesSetTestCase(  # noqa: WPS431
                    CaseType,  # type: ignore[valid-type, misc]
                ):
                    databases = dbs
                CaseType = DatabasesSetTestCase  # noqa: N806
            test_case: TransactionTestCase = CaseType(methodName='__init__')
            test_case._pre_setup()  # type: ignore[attr-defined] # noqa: WPS437
            request.addfinalizer(
                test_case._post_teardown,  # type: ignore[attr-defined] # noqa: WPS437
            )

        return factory

مع ذلك يمكن للمرء استخدام شيء مثل:

class TestCase(object):

    def test_ok(
        self,
        db_case,
        ...
    ):
        db_case(dbs=('non_default_db_alias',))

يجب استخدام هذا بدلاً من db fixture أو pytest.mark.django_db mark.

jcushman ، هل تمانع في مشاركة

إضافة الرد في حالة اصطدام أي شخص آخر بهذا.

تحتاج إلى إضافة الكود أدناه إلى conftest.py . يتم التقاط هذه المباراة تلقائيًا لذا لا داعي لإضافتها إلى الاختبار.

"" الحمر
def pytest_sessionstart (جلسة):
من django.test استيراد TransactionTestCase

TransactionTestCase.databases = set(settings.DATABASES.keys())

""

لذلك أعتقد أن هذه المشكلة تنبع من الدعم المناسب متعدد ديسيبل. سأغلق هذه المشكلة باعتبارها نسخة مكررة من ذلك.

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