pytest-Django tidak membersihkan di antara pengujian saat menggunakan Django dengan banyak basis data. Masalahnya terkait dengan pertanyaan StackOverflow ini: http://stackoverflow.com/questions/10121485/Django-testcase-not-using-transactions-on-secondary-database
Perlengkapan db
menggunakan TestCase Django di bawah penutup.
Berikut adalah kode dari perlengkapan db
:
case = TestCase(methodName='__init__')
case._pre_setup()
request.addfinalizer(case._post_teardown)
request.addfinalizer(_django_cursor_wrapper.disable)
Inilah cara saya menambalnya sebagai solusi dalam kode saya:
case = TestCase(methodName='__init__')
case.multi_db = True
case._pre_setup()
request.addfinalizer(case._post_teardown)
request.addfinalizer(_django_cursor_wrapper.disable)
Jelas bahwa flag multi_db ada dan default ke False karena suatu alasan. Saya tidak yakin cara yang tepat untuk memasukkan dukungan multi_db ke pytest_Django, dan saya tidak yakin cara yang tepat untuk menguji perubahan seperti itu.
Jika Anda memiliki saran, saya dapat mengerjakan permintaan tarik.
Bisakah Anda memberikan contoh tes menggunakan versi tambalan Anda yang menggunakan dua basis data? Saya pikir itu akan membantu memahami bagaimana banyak basis data digunakan dan apa yang Anda perlu kendalikan selain mengatur multi_db ke true.
DATABASES = {
'default': {
'ENGINE' : 'django.db.backends.sqlite3',
'NAME' : 'default.db',
},
'operations': {
'ENGINE' : 'django.db.backends.sqlite3',
'NAME' : 'operations.db',
},
}
Sebagian besar/semua model saya dikaitkan dengan "operasi" basis data kedua. Saya percaya TestCase Django akan menyiram transaksi dengan benar pada db default, tetapi tidak yang lain kecuali multi_db=True
.
Berikut adalah beberapa contoh tes:
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
Ini adalah output dengan perlengkapan db
saya yang ditambal, dijelaskan di atas:
$ 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 =====================================
Ini adalah output dengan perlengkapan db
milik pytest_Django sendiri:
$ 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 ===============================
Jadi dari tampilan cepat sepertinya perlengkapan multi_db
dan pytest.mark.django_db(multi=True)
dapat berfungsi sebagai API? Adakah orang lain yang punya ide yang lebih baik?
Itu terdengar masuk akal. Saya tentu tidak punya ide lain, lebih baik atau sebaliknya.
Halo. Apakah ada diskusi tentang dukungan multi_db
selain masalah ini? Dokumen mengatakan bahwa seseorang harus menghubungi tetapi apa statusnya saat ini?
@slafs Belum banyak diskusi/permintaan untuk dukungan multi database selain ini.
Akan sangat mudah untuk meneruskan multi_db=True
, tetapi kami juga memerlukan beberapa jenis tes untuk ini, jadi kami juga memerlukan pengaturan database sekunder. Jika ada yang mau mengerjakan ini, itu akan sangat bagus :)
Saya telah menggunakan work-around @ftobia yang disebutkan di posting pembuka, tetapi setelah memutakhirkan ke Django 1.8.4 dari 1.7 berhenti bekerja.
Ada perubahan Django untuk membuat pengaturan penanganan kode/tearDown mencari atribut kelas multi_db
alih-alih atribut instans.
Mengubah solusi @ftobia menjadi yang berikut ini berhasil untuk saya:
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:
Ini menghentikan kami dari beralih dari pelari uji Django ke pytest.
Mengapa tidak menggunakan django_case.multi_db = True
sini https://github.com/pytest-dev/pytest-Django/blob/master/pytest_django/fixtures.py#L107 secara default?
+1
+1
Saya pikir memiliki penanda pada penanda Django_db seperti itu akan menjadi API yang baik:
@pytest.mark.django_db(transactional=True, multi_db=True)
def test_a():
pass
Implementasinya harus relatif mirip dengan reset_sequences
(PR #308) atau serialized_rollback
(PR #353).
Bagian "rumit" adalah meningkatkan rangkaian pengujian internal pytest-Django untuk memuat banyak basis data.
Jika seseorang ingin bekerja dalam hal ini, saya pasti akan meninjaunya dan membantu menggabungkannya. Jangan ragu untuk mulai mengerjakan PR jika ini adalah sesuatu yang menarik bagi Anda!
Meninggalkan beberapa komentar di https://github.com/pytest-dev/pytest-Django/pull/397#issuecomment -261987751, dan membuat PR, yang memungkinkan untuk mengubah ini secara umum: https://github. com/pytest-dev/pytest-Django/pull/431.
Opsi lain untuk mengaktifkan multi_db bagi mereka yang mencari solusi sementara. Letakkan ini di conftest.py
def pytest_sessionstart(session):
from django.test import TransactionTestCase
TransactionTestCase.multi_db = True
Untuk orang lain yang mencari solusi, perhatikan bahwa multi_db
tidak digunakan lagi di Django 2.2. Pengganti yang berhasil untuk saya adalah:
TransactionTestCase.databases = set(settings.DATABASES.keys())
@jcushman apakah Anda keberatan membagikan perlengkapan lengkap atau cuplikan kode yang Anda gunakan?
Untuk alasan apa pun, pytest_sessionstart()
di basis kami conftest.py
tidak melakukan apa-apa. Mengambil halaman dari #342 bersama dengan standar pytest monkeypatch
barang dan telah mendarat di berikut ini untuk penggunaan multi-database kami:
# 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()
_Edit: kami pindah ke perlengkapan sesi._
Agar tidak kehilangan kekuatan super pytest dengan beralih ke kasus uji Django unittest, saya menyalin dan menambal internal pytest-Django ke peretasan jelek (belum berfungsi!) ini:
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
Dengan itu seseorang dapat menggunakan sesuatu seperti:
class TestCase(object):
def test_ok(
self,
db_case,
...
):
db_case(dbs=('non_default_db_alias',))
Ini harus digunakan sebagai ganti perlengkapan db
atau tanda pytest.mark.django_db
.
@jcushman apakah Anda keberatan membagikan perlengkapan lengkap atau cuplikan kode yang Anda gunakan?
Menambahkan balasan jika ada orang lain yang menabrak ini.
Anda perlu menambahkan kode di bawah ini ke dalam conftest.py
. Perlengkapan ini diambil secara otomatis sehingga tidak perlu menambahkannya ke tes.
```py
def pytest_sessionstart(sesi):
dari django.test impor TransactionTestCase
TransactionTestCase.databases = set(settings.DATABASES.keys())
````
Jadi saya percaya masalah ini bermuara pada dukungan multi-db yang tepat. Saya akan menutup masalah ini sebagai duplikat dari itu.
Komentar yang paling membantu
Opsi lain untuk mengaktifkan multi_db bagi mereka yang mencari solusi sementara. Letakkan ini di conftest.py