Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/ruby/24.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python [py]在嵌套范围内使用SQLAlchemy嵌套事务测试Flask应用程序_Python_Flask_Sqlalchemy_Flask Sqlalchemy_Pytest - Fatal编程技术网

Python [py]在嵌套范围内使用SQLAlchemy嵌套事务测试Flask应用程序

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测试中有嵌套的数据库访问作用域,这样我就可以使用
模块
作用域来生成几乎是静态的、需要很长时间才能生成的数据库内容,以及
函数
作用域来生成需要为每个测试创建和回滚的内容。在类似这样的半伪代码中:

@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()
是否关闭所有子事务?那么模块范围的夹具是如何工作的

如果你能分享一些关于这方面的见解,非常感谢

另外,我最初的模块范围夹具主要基于以下内容:。在应该的地方给道具