لا يقوم 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 على 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())
""
لذلك أعتقد أن هذه المشكلة تنبع من الدعم المناسب متعدد ديسيبل. سأغلق هذه المشكلة باعتبارها نسخة مكررة من ذلك.
التعليق الأكثر فائدة
خيار آخر لتمكين multi_db لمن يبحثون عن حل مؤقت. ضع هذا في conftest.py