2017-10-19 35 views
0

J'utilise Pytest pour tester une application Flask + SQLAlchemy. Ceci est le contenu de tests/contftest.pyRecursionError: profondeur de récursivité maximale dépassée dans Flask Pytest

import pytest 
from sqlalchemy import create_engine 
from sqlalchemy.orm import scoped_session, sessionmaker 
from flask import _app_ctx_stack 
from flask.ext.sqlalchemy import SQLAlchemy, BaseQuery 
from package.myapp import create_app 
from package.config import DefaultConfig 

DbSession = scoped_session(
     sessionmaker(), 
     scopefunc=_app_ctx_stack.__ident_func__ 
    ) 
@pytest.fixture(scope='session') 
def app(request): 
    _app = create_app() 
    _app.debug = False 

    _app.engine = create_engine(_app.config['SQLALCHEMY_DATABASE_URI'], connect_args={"options": "-c timezone=utc"}) 
    global DbSession 
    DbSession.configure(bind=_app.engine, query_cls=BaseQuery) 

    # Establish an application context before running the tests. 
    ctx = _app.app_context() 
    ctx.push() 

    @_app.teardown_appcontext 
    def teardown(exception=None): 
     ctx.pop() 
     global DbSession 
     if DbSession: 
      DbSession.remove() 


    request.addfinalizer(teardown) 
    return _app 

Quand je lance pytest, je reçois ce message d'erreur

___________________ ERROR at teardown of test_create_project ___________________ 

exception = None 

    @_app.teardown_appcontext 
    def teardown(exception=None): 
>  ctx.pop() 

tests/conftest.py:31: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../.virtualenvs/quest-backend/lib/python3.6/site-packages/flask/ctx.py:189: in pop 
    self.app.do_teardown_appcontext(exc) 
../../.virtualenvs/quest-backend/lib/python3.6/site-packages/flask/app.py:1892: in do_teardown_appcontext 
    func(exc) 
tests/conftest.py:31: in teardown 
    ctx.pop() 
E RecursionError: maximum recursion depth exceeded 
!!! Recursion detected (same locals & position) 

Répondre

1

L'expansion de la réponse de Roman Kutlák, voici comment je réécrire votre appareil app:

@pytest.fixture(scope='session') 
def app(): 
    _app = create_app() 
    _app.debug = False 

    _app.engine = create_engine(_app.config['SQLALCHEMY_DATABASE_URI'], connect_args={"options": "-c timezone=utc"}) 
    # session should probably not be global?.. 
    DbSession = scoped_session(
     sessionmaker(), 
     scopefunc=_app_ctx_stack.__ident_func__ 
    ) 
    DbSession.configure(bind=_app.engine, query_cls=BaseQuery) 

    # Establish an application context before running the tests. 
    ctx = _app.app_context() 
    ctx.push() 

    # this function is specifically for app's teardown, don't call it again for fixture teardown 
    @_app.teardown_appcontext 
    def teardown(exception=None): 
     if DbSession: 
      DbSession.remove() 

    # here is where tests will be executed 
    yield _app 

    # now tear-down our fixture (as apposed to flask app's teardown) 
    ctx.pop() 

Il n'est pas nécessaire d'utiliser global mot-clé à moins que vous souhaitez attribuer une valeur à une variable globale à l'intérieur portée intérieure.

3

Je ne vous crois pas censé appeler ctx.pop() dans votre fonction teardown que l'appel à pop() invoque le callback teardown enregistrés (d'où l'RECUR infinie AppContext appelle Flask.do_teardown_appcontext() qui contient les éléments suivants:

for func in reversed(self.teardown_appcontext_funcs): 
    func(exc) 

Vous devez appeler le ctx.pop() lorsque vous détruisez votre appareil.