Python [py]在嵌套范围内使用SQLAlchemy嵌套事务测试Flask应用程序
我希望在pytest测试中有嵌套的数据库访问作用域,这样我就可以使用Python [py]在嵌套范围内使用SQLAlchemy嵌套事务测试Flask应用程序,python,flask,sqlalchemy,flask-sqlalchemy,pytest,Python,Flask,Sqlalchemy,Flask Sqlalchemy,Pytest,我希望在pytest测试中有嵌套的数据库访问作用域,这样我就可以使用模块作用域来生成几乎是静态的、需要很长时间才能生成的数据库内容,以及函数作用域来生成需要为每个测试创建和回滚的内容。在类似这样的半伪代码中: @pytest.fixture(scope='module') def module_db_session(): # create db session session = db.create() yield session # roll back al
模块
作用域来生成几乎是静态的、需要很长时间才能生成的数据库内容,以及函数
作用域来生成需要为每个测试创建和回滚的内容。在类似这样的半伪代码中:
@pytest.fixture(scope='module')
def module_db_session():
# create db session
session = db.create()
yield session
# roll back all the changes after the module is tested
session.rollback()
@pytest.fixture(scope='function')
def function_db_session(module_db_session):
# Create a nested transaction that is used for this function scope
nested_transaction = module_db_session.create()
yield nested_transaction
# roll back all the changes in the nested transaction after the function is tested
nested_transaction.rollback()
# Theoretical usage
@pytest.fixture(scope='module')
def createstuff(module_db_session):
# Creates a lot of stuff that takes a lot of time to generate
# and is static data in the database so generating it once for module is nice
for i in range(1000000000):
module_db_session.create_thing()
@pytest.fixture(scope='function')
def creatething(createstuff, function_db_session):
# Creates much less stuff that is actively tested and changes from test to test
# so the scope should be function here
function_db_session.create_another_thing() # this should be rolled back after the test runs
很明显,我尝试在真实代码中做同样的事情,但是SQLAlchemy事务和会话对我来说还是有点太陌生了。以下是我现在拥有的:
@pytest.fixture(scope='module')
def module_db_session(app, db):
"""
Returns function-scoped session.
"""
with app.app_context():
conn = db.engine.connect()
transaction = conn.begin()
options = {'bind': conn, 'binds': {}}
sess = database.create_scoped_session(options=options)
# establish a SAVEPOINT just before beginning the test
# (http://docs.sqlalchemy.org/en/latest/orm/session_transaction.html#using-savepoint)
sess.begin_nested()
@event.listens_for(sess(), 'after_transaction_end')
def restart_savepoint(sess2, trans):
# Detecting whether this is indeed the nested transaction of the test
if trans.nested and not trans._parent.nested:
# The test should have normally called session.commit(),
# but to be safe we explicitly expire the session
sess2.expire_all()
sess.begin_nested()
db.session = sess
yield sess
# Cleanup
# This instruction rolls back any commit that was executed in the tests.
transaction.rollback()
sess.remove()
conn.close()
@pytest.fixture(scope='function')
def db_session(app, module_db_session):
"""
Returns function-scoped session.
"""
with module_db_session.begin(subtransactions=True) as parent_transaction:
conn = module_db_session.connection()
options = {'bind': conn, 'binds': {}}
sess = database.create_scoped_session(options=options)
with sess.begin_nested() as nested_transaction:
@event.listens_for(sess(), 'after_transaction_end')
def restart_savepoint(sess2, trans):
if trans.nested and trans._parent.nested and not trans._parent._parent.nested:
# The test should have normally called session.commit(),
# but to be safe we explicitly expire the session
sess2.expire_all()
sess.begin_nested()
yield sess
parent_transaction.rollback()
我尝试了很多不同的方法,但现在几乎每一种方法我都得到了sqlalchemy.exc.ResourceClosedError:在函数范围的会话中,对于parent\u事务
,此事务已关闭,但我不完全确定原因。我最初在函数范围的fixture中只有一个嵌套事务,但由于db.session.commit()
关闭子事务(如果您在其中),因此我创建了两个级别的嵌套事务,并在每次.commit()
关闭一个时返回一个新的嵌套事务。。。因此,当我尝试.rollback()
时,我真的不明白为什么我的父事务在最后关闭了。.commit()
是否关闭所有子事务?那么模块范围的夹具是如何工作的
如果你能分享一些关于这方面的见解,非常感谢
另外,我最初的模块范围夹具主要基于以下内容:。在应该的地方给道具