Pytest-django: Wie verwende ich die django_db-Markierung bei einem Sitzungs-Fixture?

Erstellt am 14. Sept. 2017  ·  15Kommentare  ·  Quelle: pytest-dev/pytest-django

Ich habe eine Situation, in der ich ein riesiges DB-Fixture laden muss, das von einer Funktion erstellt wird. Das Fixture wird in allen API-Tests benötigt. Also habe ich ein Session-Fixture bei conftest.py gemacht, was es tun würde. Aber das Problem ist, dass pytest die folgende Ausnahme auslöst, obwohl ich django_db markiert habe:

E Failed: Database access not allowed, use the "django_db" mark to enable it.
Unten ist mein Code-Snippet.

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

Hilfreichster Kommentar

Dies war für mich der größte Schmerzpunkt, als ich von Djangos klassenbasierten UnitTest-Tests zu pytest-django kam – in Django verwenden wir setUpTestData , um teure DB-Operationen einmal auszuführen (äquivalent zu sitzungsbezogenen Pytest-Fixtures). und dann gibt es einen schlauen Trick, obj.refresh_from_db() in setUp auszuführen, um die Klassenreferenzen zu aktualisieren.

Selbst wenn ich nur eine DB-Modellinstanz erstelle und sie bei jedem TC neu lade, ist dies fast immer schneller als die Erstellung in jedem Testfall.

Es wäre großartig, wenn wir die sitzungsbezogenen Fixtures von pytest-tipsi-django stromaufwärts zusammenführen könnten, falls dies möglich ist; Es dauerte ein bisschen zu graben, um dieses Problem und die Lösung zu finden.

Alle 15 Kommentare

@ludbek wir haben diese Funktion auch vermisst und ein Plugin für diese Funktion erstellt:
https://github.com/tipsi/pytest-tipsi-django (Verwendung: https://github.com/tipsi/pytest-tipsi-django/blob/master/test_django_plugin/app/tests/test_transactions.py)
In Verbindung mit:
https://github.com/tipsi/pytest-tipsi-testing
Es gibt Ihnen die Möglichkeit, Transaktionen zu verschachteln und die Ausführungs-/Abschaltreihenfolge zu korrigieren.

@cybergrind Danke für die Antwort. Ich werde es auf jeden Fall überprüfen und berichten, wie es gelaufen ist.

Dies war für mich der größte Schmerzpunkt, als ich von Djangos klassenbasierten UnitTest-Tests zu pytest-django kam – in Django verwenden wir setUpTestData , um teure DB-Operationen einmal auszuführen (äquivalent zu sitzungsbezogenen Pytest-Fixtures). und dann gibt es einen schlauen Trick, obj.refresh_from_db() in setUp auszuführen, um die Klassenreferenzen zu aktualisieren.

Selbst wenn ich nur eine DB-Modellinstanz erstelle und sie bei jedem TC neu lade, ist dies fast immer schneller als die Erstellung in jedem Testfall.

Es wäre großartig, wenn wir die sitzungsbezogenen Fixtures von pytest-tipsi-django stromaufwärts zusammenführen könnten, falls dies möglich ist; Es dauerte ein bisschen zu graben, um dieses Problem und die Lösung zu finden.

Hallo @paultiplady

Ich bin mir nicht sicher, ob der Ansatz von pytest-tipsi-djanjo zum üblichen Testmodell für pytest $ passt. Der auffälligste Unterschied ist das Finishing von Fixtures: Derzeit beendet pytest nicht explizit unnötige Fixtures mit einem größeren Umfang. Daher müssen Sie Transaktionen explizit in einer bestimmten Reihenfolge beenden und im Allgemeinen kann dies sehr unterschiedliche Auswirkungen haben (derzeit kann pytest Fixtures aktiv halten, selbst wenn der aktive Test und seine Fixtures dies überhaupt nicht erfordern).

Wir mussten die Tests in unserem Projekt auf diesen Ansatz umstellen, weil wir manchmal einige große Szenarien testen müssen und wir das vorhandene manuelle Transaktionsmanagement in großen Tests durch etwas bessere Fixtures ersetzt haben, aber es erfordert immer noch Aufmerksamkeit bei Auftragstests.

Im Moment sehe ich dafür nur eine Lösung: eine Art FAQ in die Dokumentation aufnehmen.

Danke für das zusätzliche Detail @cybergrind. Ich habe mich ein bisschen mehr damit beschäftigt, aber für heute ist die Zeit abgelaufen - hier muss ich hin, ich würde mich über eine Plausibilitätsprüfung freuen, ob dieser Ansatz nützlich ist oder nicht, da ich es bin nicht so vertraut mit den pytest-Interna.

Ich verstehe auch nicht, was Sie mit "pytest beendet unnötige Fixtures nicht explizit mit einem größeren Umfang" meinen. Könnten Sie das bitte etwas weiter ausführen? Bezieht sich das auf Finalizer? Das könnte sich auf das auswirken, was ich unten geschrieben habe.

Das pytest-django-Plugin verwendet die Markierung django_db , die in _django_db_marker in plugin.py (https://github.com/pytest-dev/pytest-django/blob/master/pytest_django/plugin.py #L375) und ruft das funktionsbezogene Fixture db (https://github.com/pytest-dev/pytest-django/blob/master/pytest_django/fixtures.py#L142). Dieses Fixture instanziiert einen Django-Testfall und ruft seine Funktion _pre_setup (und stellt die _post_teardown in die Warteschlange).

Ich sehe ein paar Optionen:

Ich frage mich, ob wir _django_db_marker erweitern könnten, um optional Unterrichts- oder Sitzungs-
auch bereichsbezogenes Setup, das im Wesentlichen dasselbe tun würde wie db , aber stattdessen das Äquivalent von cls.setUpTestData oder eine Funktion aufruft, die in den Mark-Kwargs übergeben wird.

Für Class-Scoped, und ich nehme auch für Session-Scoped an, wäre das erwartete Verhalten, die DB danach zurückzusetzen, also brauchen wir im Grunde Scoped-Fixtures, die in der richtigen Reihenfolge auslösen und die jeweils ihre eigene Atomic einrichten Transaktion. Ich glaube, das bedeutet, dass dies eine Modifikation des db -Geräts sein muss und nicht ein separates Gerät, das daneben läuft.

Auf diese Weise würden wir jedes angegebene Setup auf Klassen-/Sitzungsebene korrekt auslösen, und dieses Setup würde einmal pro Klasse/Sitzung aufgerufen. Ich glaube, dass eine Änderung an der Markierung selbst erforderlich ist, denn wenn Sie nur eine sitzungsbezogene Funktion einrichten, die django_db_blocker.unblock() manuell auslöst, scheint dies zu geschehen, nachdem die Markierung django_db die erste Transaktion eingerichtet hat.

Das könnte etwa so aussehen (in 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')

Ist das verrücktes Gerede oder lohnt es sich, diesen Thread weiter zu untersuchen?

Zur Finalisierung: https://github.com/tipsi/pytest-tipsi-testing/blob/master/tests/test_finalization.py

Dieser Test funktioniert nicht ohne explizite Finalisierung, genauso wie Datenbank-Fixtures auf Nicht-Funktionsebene. Und hier geht es um die pytest-Implementierung, also gibt es in pytest-django nichts zu tun, um das Problem zu beheben.

Duplikat von Nr. 388 und Nr. 33

Danke, schließen.

33 ist geschlossen und #388 enthält keine sinnvolle Diskussion (im Gegensatz zu dieser). Scheint seltsam, dieses @blueyed zu schließen, wenn überhaupt, würde ich vorschlagen, # 388 zu schließen und dieses zum kanonischen Ticket für dieses Problem zu machen.

👍 danke!

Über diese Funktion würde ich mich auch sehr freuen.

@mkokotovich
Wie viel? Genug, um es selbst zu verwirklichen? ;)

Wie auch immer, das Haupt- / Grundproblem hier (aus dem ursprünglichen Kommentar) ist bereits, dass die DB während der Tests zurückgesetzt wird, also gibt es keine triviale Möglichkeit, ein solches Fixture mit Session-Bereich zu haben.

Was jedoch funktionieren könnte, ist etwas entlang:

@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

Die Idee ist, alles in einen zusätzlichen Atomblock zu packen. Dies ist jedoch ungetestet, und Sie müssen möglicherweise tatsächlich TransactionTestCase dafür verwenden.

@paultiplay
Ihr Kommentar klingt gut - dh es lohnt sich, AFAICS weiter zu untersuchen (siehe auch meinen vorherigen Kommentar).

@blueyed Wir verwenden diesen Ansatz seit mehr als einem Jahr (packen Sie alles in einen zusätzlichen Atomblock), es funktioniert ziemlich gut.
Aber Sie können so etwas nicht einfach einwerfen, da pytest nicht über die Deterministik verfügt, wo die Sitzungsebene (und andere höher als Test) geschlossen wird, sodass Abhängigkeiten von DB-Transaktionen nachverfolgt werden müssen, unabhängig davon, ob sie sich gegenseitig benötigen oder nicht direkt .
Sie müssen also den Transaktionsstapel explizit verfolgen und verschachtelte Transaktionen vor dem nächsten Test schließen. Dies kann folgendermaßen erfolgen: https://github.com/tipsi/pytest-tipsi-django/blob/master/pytest_tipsi_django/django_fixtures.py#L46

Entschuldigung, ich möchte das klarstellen, da ich glaube, dass ich auf das gleiche Problem stoße:

Wenn ich ein Fixture erstellen möchte, das auf der Erstellung eines neuen DB-Objekts basiert, dachte ich, ich könnte es tun

@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

Ich erhalte jedoch beim Ausführen des Testfalls die folgende Fehlermeldung: Database access not allowed, use the "django_db" mark, or the "db" or "transactional_db" fixtures to enable it.

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen