Pytest-django: pytest.mark.django_db ne joue pas bien avec setup_module()

Créé le 14 sept. 2013  ·  15Commentaires  ·  Source: pytest-dev/pytest-django

Voici mon code de test :

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

Et je reçois DatabaseError: relation "test" does not exist partir de test_fetch_val() . je reçois aussi

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

sortie capturée. On dirait que setup_module() est appelé avant la création de la base de données.

Commentaire le plus utile

+1 Pour moi, passer simplement de certains tests unitaires initiaux à Djano, c'est un inconvénient majeur lors de l'initialisation de la base de données pour mes cas de test d'API. En fait, la prise en charge des appareils de base de données de session/module devrait être une fonctionnalité de base. Des suggestions comment résoudre cela?

Tous les 15 commentaires

La marque django_db est basée sur les appareils, je ne sais pas comment communiquer entre les appareils et les méthodes setup_*.

Pourriez-vous utiliser un appareil à portée de module à la place pour obtenir la même chose ? C'est-à-dire quelque chose comme

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

Je viens d'essayer. N'a pas fonctionné. Je reçois Database access not allowed, use the "django_db" mark to enable maintenant.
De plus, test_table() ne s'exécute pas (j'ai essayé de lever une exception ici)

Désolé, cela devrait être usefixtures, pas usefixture :

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

Maintenant, je reçois ScopeMismatchError: You tried to access the 'function' scoped funcarg 'db' with a 'module' scoped request object, involved factories .

La suppression du paramètre db du paramètre entraîne l'échec de l'appareil, la suppression de scope='module' fait fonctionner les réflexions, mais alors test_table() est exécuté pour chaque test, ce que je ne veux pas.

j'ai aussi essayé

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

qui fonctionne presque, mais le deuxième test échoue d'une manière ou d'une autre avec DatabaseError: relation "test" does not exist . Un mystère complet pour moi.

Django et le marqueur django_db fonctionnent de la même manière que chaque cas de test s'exécute dans sa propre transaction, donc utiliser django_db avec un appareil de niveau module qui insère des données dans la base de données n'a pas vraiment de sens.

Quel est votre cas d'utilisation ici ? Comment se fait-il que vous créez une table de base de données "à la main" dans vos tests, et non avec Django, mais que vous souhaitiez quand même utiliser le curseur de base de données de test/base de données de test de Django ?

Ne pourriez-vous pas atteindre ce que vous recherchez en construisant simplement un curseur de base de données simple ?

(Le dernier exemple évite le ScopeMismatchError, mais il échoue dans le test à la place parce que la transaction est annulée après le premier test, d'où l'annulation de la table de test.)

J'essaie de tester quelques utilitaires de base de données de bas niveau, qui utilisent le curseur django en interne - https://github.com/Suor/handy/blob/master/handy/db.py#L40

Et je trouve qu'il sera beaucoup plus compliqué de définir un modèle en models.py , puis des appareils dans un format django pour tester cela. Ce serait 3 fichiers au total pour tester des fonctionnalités assez basiques.

Je veux juste exécuter du code d'initialisation avant d'exécuter des tests afin d'avoir des données avec lesquelles jouer.

Oh, j'ai réussi ! L'encapsulation du SQL d'initialisation dans begin; ... commit; aidé.

Un grand merci pour tous les conseils que vous avez fournis.

C'est un hack qui m'a l'air un peu fragile. Je mettrais probablement en place une application supplémentaire qui ne serait utilisée que pour les tests avec un modèle simple que vous pourrez ensuite utiliser pour exécuter ces fonctions. Vous pouvez alors facilement utiliser l'ORM de Django pour remplir les données de vos tests. C'est un peu plus de frappe et quelques fichiers supplémentaires, mais cela devrait être sûr pour les futures casses.

Si quelqu'un d'autre rencontre ce problème : effectuer la configuration de la base de données dans setup_function/setup_class/setup_module n'est pas vraiment pris en charge ou possible de manière satisfaisante car la configuration de la base de données de pytest-django est basée sur des appareils.

La solution est d'utiliser un appareil qui demande correctement l'appareil db :
http://pytest-django.readthedocs.org/en/latest/helpers.html#db

@pelme
Avec le dispositif db , il est possible d'exécuter une telle configuration sur la portée de la fonction uniquement, pas sur la classe, le module ou la session. Par exemple, si nous devons remplir certains enregistrements dans la base de données avant l'exécution des tests de classe.

==================================== 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)

Y a-t-il un autre moyen de le gérer?

Oui, c'est une limitation de l'appareil db tel qu'il est actuellement implémenté. Dans PR #258, il y a eu du travail pour rendre possible la création d'un état de base de données lié par la portée de classe/module/session.

Même si ce problème est assez ancien, j'ai trouvé utile de trouver la solution à un problème que j'avais avec pytest et pytest-django . Depuis pytest 3.5.0 il y a un problème lors de l'utilisation django_db marque module et des appareils de niveau

Ci-dessous fonctionnait avant 3.5.0 (au moins, cela ne causait aucun problème).

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

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

Après 3.5.0 il relance

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

La solution consistait à supprimer scope='module' .

+1 Pour moi, passer simplement de certains tests unitaires initiaux à Djano, c'est un inconvénient majeur lors de l'initialisation de la base de données pour mes cas de test d'API. En fait, la prise en charge des appareils de base de données de session/module devrait être une fonctionnalité de base. Des suggestions comment résoudre cela?

+1

Cette page vous a été utile?
0 / 5 - 0 notes