Pytest-django: pytest.mark.django_db spielt nicht gut mit setup_module()

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

Hier mein Testcode:

import pytest
pytestmark = pytest.mark.django_db

from handy.db import do_sql, fetch_val

def setup_module(fetch_val):
    print 1
    do_sql('''
        create table test (
            id int primary key,
            tag int not null
        );

        insert into test values (1, 10), (2, 20);
    ''')
    print 2

def test_fetch_val():
    print 3
    assert fetch_val('select min(id) from test') == 1

Und ich bekomme DatabaseError: relation "test" does not exist von test_fetch_val() . ich bekomme auch

2
Creating test database for alias 'default'...
3

erfasste Ausgabe. Es sieht so aus, als ob setup_module() aufgerufen wird, bevor die Datenbank erstellt wird.

Hilfreichster Kommentar

+1 Für mich, nur von einigen anfänglichen Djano-Komponententests zu wechseln, ist dies ein großer Nachteil bei der Initialisierung der Datenbank für meine API-Testfälle. Tatsächlich sollte die Unterstützung von Session-/Modul-Datenbank-Fixtures eine grundlegende Funktion sein. Irgendwelche Vorschläge, wie man das löst?

Alle 15 Kommentare

Die django_db-Markierung basiert auf Fixtures, ich bin mir nicht sicher, wie man zwischen Fixtures und den setup_*-Methoden kommuniziert.

Könnten Sie stattdessen ein modulbezogenes Gerät verwenden, um dasselbe zu erreichen? Dh sowas wie

pytestmark = pytest.mark.usefixture('test_table')

@pytest.fixture(scope='module')
def test_table(db):
    do_sql('create table ...')

def test_fetch_val():
    assert fetch_val('...') == 1

Gerade versucht. Hat nicht funktioniert. Ich bekomme jetzt Database access not allowed, use the "django_db" mark to enable .
Außerdem wird test_table() nicht ausgeführt (ich habe versucht, dort eine Ausnahme auszulösen)

Sorry, es sollte usefixtures sein, nicht usefixture:

http://pytest.org/latest/fixture.html#usefixtures

Jetzt bekomme ich ScopeMismatchError: You tried to access the 'function' scoped funcarg 'db' with a 'module' scoped request object, involved factories .

Das Entfernen des Parameters db aus dem Parameter führt zu einem Fixture-Fehler, das Entfernen von scope='module' make think work, aber dann wird test_table() für jeden Test ausgeführt, was ich nicht möchte.

hab ich auch probiert

pytestmark = pytest.mark.django_db

@pytest.fixture(scope='module')
def test_table():
    do_sql('''
        create table test (
            id int primary key,
            tag int not null
        );

        insert into test values (1, 10), (2, 20);
    ''')

def test_fetch_val(test_table):
    assert fetch_val('select min(id) from test') == 1

def test_fetch_val2(test_table):
    assert fetch_val('select min(id) from test') == 1

was fast funktioniert, aber der zweite Test schlägt irgendwie mit DatabaseError: relation "test" does not exist fehl. Für mich ein absolutes Mysterium.

Django und der django_db-Marker funktionieren so, dass jeder Testfall in seiner eigenen Transaktion ausgeführt wird, daher macht es nicht wirklich Sinn, django_db mit einem Fixture auf Modulebene zu verwenden, das Daten in die Datenbank einfügt.

Was ist Ihr Anwendungsfall hier? Wie kommt es, dass Sie in Ihren Tests eine Datenbanktabelle "von Hand" erstellen und nicht mit Django, aber trotzdem Djangos Testdatenbank/Testdatenbank-Cursor verwenden möchten?

Könnten Sie nicht das erreichen, wonach Sie suchen, indem Sie einfach einen einfachen Datenbank-Cursor erstellen?

(Das letzte Beispiel vermeidet den ScopeMismatchError, schlägt aber stattdessen im Test fehl, weil die Transaktion nach dem ersten Test zurückgesetzt wird, wodurch die Testtabelle zurückgesetzt wird.)

Ich versuche, ein paar Low-Level-DB-Dienstprogramme zu testen, die den Django-Cursor intern verwenden - https://github.com/Suor/handy/blob/master/handy/db.py#L40

Und ich finde es viel chaotischer, ein Modell in models.py und dann Fixtures in einem Django-Format zu definieren, um das zu testen. Das wären insgesamt 3 Dateien, um die grundlegende Funktionalität zu testen.

Ich möchte nur einen Initialisierungscode ausführen, bevor ich Tests ausführe, damit ich einige Daten zum Spielen habe.

Oh, ich habe es geschafft! Das Einpacken von Initialisierungs-SQL in begin; ... commit; geholfen.

Vielen Dank für alle Tipps, die Sie gegeben haben.

Das ist ein Hack, der für mich etwas zerbrechlich aussieht. Ich würde wahrscheinlich eine zusätzliche App einrichten, die nur für Tests mit einem einfachen Modell verwendet wird, mit dem Sie dann diese Funktionen ausführen können. Sie können dann einfach das ORM von Django verwenden, um die Daten in Ihre Tests einzufügen. Es ist etwas mehr Tippen und ein paar zusätzliche Dateien, aber dann sollte es für zukünftige Brüche sicher sein.

Falls noch jemand dieses Problem findet: Das Datenbank-Setup in setup_function/setup_class/setup_module wird nicht wirklich unterstützt oder ist in irgendeiner Weise möglich, da das Datenbank-Setup von pytest-django auf Fixtures basiert.

Die Lösung besteht darin, ein Fixture zu verwenden, das das db-Fixture ordnungsgemäß anfordert:
http://pytest-django.readthedocs.org/en/latest/helpers.html#db

@pelme
Mit dem db Fixture ist es möglich, ein solches Setup nur auf Funktionsumfang auszuführen, nicht auf Klasse, Modul oder Session. Zum Beispiel, wenn wir einige Datensätze in die Datenbank eingeben müssen, bevor Klassentests ausgeführt werden.

==================================== ERRORS ====================================
___________ ERROR at setup of TestHistoryAlerts.test_one_point_data ____________
ScopeMismatch: You tried to access the 'function' scoped fixture 'db' with a 'session' scoped request object, involved factories
tests/conftest.py:140:  def ts(db)
../../../../.virtualenvs/app/lib/python2.7/site-packages/pytest_django/fixtures.py:158:  def db(request, _django_db_setup, _django_cursor_wrapper)

Gibt es eine andere Möglichkeit, damit umzugehen?

Ja, das ist eine Einschränkung des db-Fixtures, wie es derzeit implementiert ist. In PR #258 wurde daran gearbeitet, es möglich zu machen, einen Datenbankstatus zu erstellen, der an den Geltungsbereich von Klassen/Modulen/Sitzungen gebunden ist.

Obwohl dieses Problem schon ziemlich alt ist, fand ich es nützlich, die Lösung für ein Problem zu finden, das ich mit pytest und pytest-django . Seit pytest 3.5.0 gibt es ein Problem bei der Verwendung von django_db Mark und module Level Fixtures.

Unten hat vor 3.5.0 funktioniert (zumindest hat es keine Probleme verursacht).

@pytest.fixture(scope='module')
def user():
    return User.objects.create(...)

@pytest.mark.django_db
def test_some_case(user):
    pass

Nach 3.5.0 steigt es

Failed: Database access not allowed, use the "django_db" mark, or the "db" or "transactional_db" fixtures to enable it.

Die Lösung bestand darin, scope='module' zu entfernen.

+1 Für mich, nur von einigen anfänglichen Djano-Komponententests zu wechseln, ist dies ein großer Nachteil bei der Initialisierung der Datenbank für meine API-Testfälle. Tatsächlich sollte die Unterstützung von Session-/Modul-Datenbank-Fixtures eine grundlegende Funktion sein. Irgendwelche Vorschläge, wie man das löst?

+1

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen