Pytest-django: セッションフィクスチャでdjango_dbマークを使用するにはどうすればよいですか?

作成日 2017年09月14日  ·  15コメント  ·  ソース: pytest-dev/pytest-django

関数によって作成された巨大なdbフィクスチャをロードする必要がある状況があります。 フィクスチャはすべてのAPIテストで必要です。 そこで、 conftest.pyでセッションフィクスチャを作成しました。 しかし、問題は、 django_dbをマークしたにもかかわらず、 pytestが次の例外をスローすることです。

E Failed: Database access not allowed, use the "django_db" mark to enable it.
以下は私のコードスニペットです。

from permission.helpers import update_permissions

pytestmark = [
        pytest.mark.django_db(transaction = True),]

@pytest.fixture(scope="session", autouse = True)
def permission(request):
        load_time_consuming_db_fixture()
db-configuration enhancement

最も参考になるコメント

これは、DjangoのUnitTestクラスベーステストからpytest-djangoに至るまでの最大の問題点でした。Djangoでは、 setUpTestDataを使用して高価なDB操作を1回実行します(セッションスコープのpytestフィクスチャと同等)。次に、 setUpobj.refresh_from_db()を実行して、クラス参照を更新するという巧妙なトリックがあります。

1つのDBモデルインスタンスを作成し、それをTCごとにリロードする場合でも、ほとんどの場合、これは各テストケースで作成するよりも高速です。

可能であれば、pytest-tipsi-djangoからセッションスコープのフィクスチャをアップストリームにマージできれば素晴らしいと思います。 この問題と解決策を見つけるには少し掘り下げました。

全てのコメント15件

@ludbekまた、そのような機能を見逃し、この機能のプラグインを作成しました:
https://github.com/tipsi/pytest-tipsi-django (使用法:https://github.com/tipsi/pytest-tipsi-django/blob/master/test_django_plugin/app/tests/test_transactions.py)
と組み合わせて:
https://github.com/tipsi/pytest-tipsi-testing
これにより、トランザクションをネストし、実行/シャットダウンの順序を修正することができます。

@cybergrind返信ありがとうございます。 私は間違いなくそれをチェックして、それがどのように進んだかをあなたに知らせます。

これは、DjangoのUnitTestクラスベーステストからpytest-djangoに至るまでの最大の問題点でした。Djangoでは、 setUpTestDataを使用して高価なDB操作を1回実行します(セッションスコープのpytestフィクスチャと同等)。次に、 setUpobj.refresh_from_db()を実行して、クラス参照を更新するという巧妙なトリックがあります。

1つのDBモデルインスタンスを作成し、それをTCごとにリロードする場合でも、ほとんどの場合、これは各テストケースで作成するよりも高速です。

可能であれば、pytest-tipsi-djangoからセッションスコープのフィクスチャをアップストリームにマージできれば素晴らしいと思います。 この問題と解決策を見つけるには少し掘り下げました。

ねえ@paultiplady

pytest-tipsi-djanjoからのアプローチが、$#$ 1 pytest #$の通常のテストモデルに適合するかどうかはわかりません。 最も顕著な違いは、器具の仕上げです。現在、 pytestは、より広い範囲で不要な器具を明示的に仕上げていません。 したがって、トランザクションを特定の順序で明示的に終了する必要があり、一般に、これは非常に異なる効果を引き起こす可能性があります(現在、アクティブなテストとそのフィクスチャがまったく必要としない場合でも、 pytestはフィクスチャをアクティブに保つ可能性があります)。

プロジェクトのテストをこのアプローチに変更する必要がありました。これは、いくつかの大きなシナリオをテストする必要があり、大規模なテストの既存の手動トランザクション管理をわずかに優れたフィクスチャに置き換えたためですが、注文テストには注意が必要です。

今のところ、そのための解決策は1つしかありません。それは、ある種のFAQをドキュメントに入れることです。

追加の詳細@cybergrindをありがとう。 私はこれについてもう少し掘り下げましたが、今日の時間が不足しています-ここに到達しました。私は正気であるかどうかを確認してください-このアプローチが役立つかどうかを確認してくださいpytestの内部にはあまり詳しくありません。

また、「pytestは不要なフィクスチャをより広い範囲で明示的に終了しない」とはどういう意味かわかりませんが、もう少し詳しく教えていただけますか? それはファイナライザーを指しているのですか? それは私が以下に書いたことに影響を与えるかもしれません。

pytest-djangoプラグインはdjango_dbマークを使用します。これは、plugin.py(https://github.com/pytest-dev/pytest-django/blob/master/pytest_django/plugin.py)の_django_db_markerで処理されます。 #L375)、関数スコープのdbフィクスチャ(https://github.com/pytest-dev/pytest-django/blob/master/pytest_django/fixtures.py#L142)を呼び出します。 このフィクスチャはDjangoTestCaseをインスタンス化し、その_pre_setup関数を呼び出します(そして_post_teardownをエンキューします)。

私はいくつかのオプションを見ることができます:

_django_db_markerを拡張して、オプションでクラスまたはセッションを実行できるかどうか疑問に思っています。
スコープ設定も同様です。これは基本的にdbと同じことを行いますが、代わりにcls.setUpTestDataまたはマークkwargsで渡された関数と同等のものを呼び出します。

クラススコープの場合、およびセッションスコープの場合も想定される動作は、後でDBをロールバックすることになるため、基本的に、正しい順序でトリガーされ、それぞれが独自のアトミックをセットアップするスコープフィクスチャが必要です。取引。 つまり、これは、その横で実行される個別のフィクスチャではなく、 dbフィクスチャの変更である必要があることを意味します。

そうすれば、指定されたクラス/セッションレベルのセットアップを正しくトリガーし、そのセットアップはクラス/セッションごとに1回呼び出されます。 django_db_blocker.unblock()を手動でトリガーするセッションスコープ関数を設定した場合、django_dbマークが最初のトランザクションを設定した後に発生するように見えるため、マーク自体を変更する必要があると思います。

これは次のようになります(plugin.py _django_db_marker()内):

    if marker:
        if marker.session_db:
            getfixturevalue(request, 'db_session')

        if marker.class_db:
            getfixturevalue(request, 'db_class')

        validate_django_db(marker)
        if marker.transaction:
            getfixturevalue(request, 'transactional_db')
        else:
            getfixturevalue(request, 'db')

これはクレイジーな話ですか、それともこのスレッドはさらに調査する価値がありますか?

ファイナライズについて: https ://github.com/tipsi/pytest-tipsi-testing/blob/master/tests/test_finalization.py

このテストは、非機能レベルのデータベースフィクスチャと同様に、明示的なファイナライズなしでは機能しません。 そして、これはpytestの実装に関するものなので、pytest-djangoで修正することは何もありません。

#388と#33の複製

ありがとう、締めくくり。

33は閉じられており、#388には意味のあるディスカッションが含まれていません(これとは異なります)。 これを@blueyedで閉じるのは奇妙に思えますが、どちらかといえば#388を閉じて、これをこの問題の正規のチケットにすることをお勧めします。

👍ありがとう!

また、この機能を高く評価します。

@mkokotovich
いくら? それを自分で実現するのに十分ですか? ;)

とにかく、ここでの主な/基本的な問題(元のコメントから)は、テスト中にDBがリセットされることです。したがって、そのようなセッションスコープのフィクスチャを作成する簡単な方法はありません。

しかし、うまくいくかもしれないのは何かです:

@pytest.fixture(scope="session")
def django_db_setup(django_db_setup, django_db_blocker):
    with django_db_blocker.unblock():
        with transaction.atomic():  # XXX: could/should use `TestCase_enter_atomics` for multiple dbs
            load_time_consuming_db_fixture()
            yield

すべてを追加のアトミックブロックにラップするというアイデア。 ただし、これはテストされていないため、実際にはTransactionTestCaseを使用する必要がある場合があります。

@paultiplady
あなたのコメントは良さそうです-つまり、AFAICSをさらに調査する価値があります(私の前のコメントも参照してください)。

@blueyedこのようなアプローチを1年以上使用しています(すべてを追加のアトミックブロックにラップします)。これは非常にうまく機能します。
しかし、pytestにはセッションレベル(およびテスト以外)が閉じられる決定論がないため、そのようなものをドロップインすることはできません。そのため、相互に必要かどうかに関係なく、dbトランザクションの依存関係を追跡する必要があります。 。
したがって、次のテストの前に、トランザクションスタックを明示的に追跡し、ネストされたトランザクションを閉じる必要があります。 このような方法で行うことができます: https ://github.com/tipsi/pytest-tipsi-django/blob/master/pytest_tipsi_django/django_fixtures.py#L46

申し訳ありませんが、同じ問題が発生していると思われるため、明確にしておきたいと思います。

新しいdbオブジェクトの作成に依存するフィクスチャを作成したい場合は、できると思いました

@pytest.mark.django_db(transaction=True)
@pytest.fixture(scope="session")
def object():
    object = Object.create_object(params)
    yield object
    // or alternatively
    object = mixer.blend('myObject')
    yield object

ただし、テストケースの実行時に次のエラーが発生します: Database access not allowed, use the "django_db" mark, or the "db" or "transactional_db" fixtures to enable it.

このページは役に立ちましたか?
0 / 5 - 0 評価