pytest-djangoは、複数のデータベースでDjangoを使用している場合、テスト間でクリーンアップされません。 問題はこのStackOverflowの質問に関連しています: http :
db
フィクスチャは、内部でDjangoのTestCaseを使用します。
元の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に組み込む正しい方法がわかりません。また、そのような変更をテストする正しい方法もわかりません。
提案があれば、プルリクエストに対応できます。
2つのデータベースを使用するパッチを適用したバージョンを使用したテストの例を教えてください。 これは、複数のデータベースがどのように使用されているか、およびmulti_dbをtrueに設定してから他のデータベースを制御する必要があるかを理解するのに役立つと思います。
DATABASES = {
'default': {
'ENGINE' : 'django.db.backends.sqlite3',
'NAME' : 'default.db',
},
'operations': {
'ENGINE' : 'django.db.backends.sqlite3',
'NAME' : 'operations.db',
},
}
ほとんど/すべてのモデルは、2番目のデータベース「操作」に関連付けられています。 DjangoのTestCaseは、デフォルトのデータベースでトランザクションを正しくフラッシュすると思いますが、 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)
がAPIとして機能するように見えますか? 他の誰かがより良いアイデアを持っていますか?
それは合理的に聞こえます。 私は確かに他のアイデアを持っていません。
こんにちは。 この問題以外にmulti_db
サポートについての議論はありますか? ドキュメントには連絡を取るべきだと書かれていますが、これに関する現在の状況はどうですか?
@slafsこれ以外に、マルチデータベースのサポートに関する議論や要求はあまりありませんでした。
multi_db=True
を渡すのは非常に簡単ですが、これには何らかのテストも必要なので、セカンダリデータベースを設定する必要もあります。 誰かがこれに取り組む気があるなら、それは素晴らしいでしょう:)
私は冒頭の投稿で回避策の@ftobiaの言及を使用してきましたが、1.7からDjango 1.8.4にアップグレードした後、動作を停止しました。
コード処理setup / tearDownがインスタンス属性ではなくmulti_db
クラス属性を検索するようにDjangoが変更されました。
@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/fixtures.py#L107デフォルトで?
+1
+1
django_dbマーカーにマーカーを付けると、APIがうまくいくと思います。
@pytest.mark.django_db(transactional=True, multi_db=True)
def test_a():
pass
実装は、 reset_sequences
(PR#308)またはserialized_rollback
(PR#353)に比較的類似している必要があります。
「トリッキー」な部分は、pytest-djangoの内部テストスイートを改善して、複数のデータベースを含めることです。
誰かがこれで働きたいのなら、私は確かにそれを見直して、それをマージするのを手伝います。 これがあなたにとって興味深いものであるならば、PRに取り掛かることを遠慮なく始めてください!
https://github.com/pytest-dev/pytest-django/pull/397#issuecomment -261987751にコメントを残し、PRを作成しました。これにより、一般的な方法でこれを変更できます:https:// github。 com / 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
はDjango2.2で非推奨になっていることに注意してください。 私のために働いた代替品は次のとおりです。
TransactionTestCase.databases = set(settings.DATABASES.keys())
@jcushman使用した完全なフィクスチャまたはコードスニペットを共有してもよろしいですか?
何らかの理由で、ベースconftest.py
pytest_sessionstart()
は何もしていませんでした。 標準のpytest monkeypatch
ものと一緒に#342からページを取得し、マルチデータベースで使用するために次のものに到達しました。
# 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の超大国を失わないようにするために、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
フィクスチャまたはpytest.mark.django_db
マークの代わりに使用
@jcushman使用した完全なフィクスチャまたはコードスニペットを共有してもよろしいですか?
他の誰かがこれにぶつかる場合に備えて返信を追加します。
以下のコードをconftest.py
に追加する必要があります。 このフィクスチャは自動的に取得されるため、テストに追加する必要はありません。
`` `py
def pytest_sessionstart(session):
django.testからimportTransactionTestCase
TransactionTestCase.databases = set(settings.DATABASES.keys())
`` ``
したがって、この問題は適切なマルチデータベースのサポートに起因すると思います。 その複製としてこの問題を閉じます。
最も参考になるコメント
一時的な解決策を探している人のためにmulti_dbを有効にする別のオプション。 これをconftest.pyに入れてください