python - 在 Flask-SQLAlchemy 中隔离 py.test 数据库 session

标签 python unit-testing flask flask-sqlalchemy pytest

我正在尝试使用 Flask-SQLAlchemy 构建一个 Flask 应用程序;我使用 pytest 来测试数据库。其中一个问题似乎是在不同测试之间创建隔离的数据库 session 。

我编写了一个最小的完整示例来突出问题,请注意 test_user_schema1()test_user_schema2() 是相同的。

文件名:test_db.py

from models import User

def test_user_schema1(session):
    person_name = 'Fran Clan'
    uu = User(name=person_name)
    session.add(uu)
    session.commit()

    assert uu.id==1
    assert uu.name==person_name

def test_user_schema2(session):
    person_name = 'Stan Clan'
    uu = User(name=person_name)
    session.add(uu)
    session.commit()

    assert uu.id==1
    assert uu.name==person_name

如果数据库在我的测试之间确实是隔离的,那么两个测试都应该通过。然而,最后的测试总是失败,因为我还没有找到使数据库 session 正确回滚的方法。

sqlalchemy_session_fail

conftest.py 根据我在 Alex Michael's blog post 中看到的内容使用以下内容,但是这个 fixture 代码会中断,因为它显然没有隔离 fixture 之间的数据库 session 。

@pytest.yield_fixture(scope='function')
def session(app, db):
    connection = db.engine.connect()
    transaction = connection.begin()

    #options = dict(bind=connection, binds={})
    options = dict(bind=connection)
    session = db.create_scoped_session(options=options)

    yield session

    # Finalize test here
    transaction.rollback()
    connection.close()
    session.remove()

出于这个问题的目的,我构建了 a gist ,其中包含复制它所需的一切;您可以使用 git clone https://gist.github.com/34fa8d274fc4be240933.git 克隆它。

我正在使用以下包...

Flask==0.10.1
Flask-Bootstrap==3.3.0.1
Flask-Migrate==1.3.0
Flask-Moment==0.4.0
Flask-RESTful==0.3.1
Flask-Script==2.0.5
Flask-SQLAlchemy==2.0
Flask-WTF==0.11
itsdangerous==0.24
pytest==2.6.4
Werkzeug==0.10.1

两个问题:

  1. 为什么现状会被打破?同样的 py.test fixture 似乎也适用于其他人。
  2. 如何解决这个问题才能正常工作?

最佳答案

Alex Michael's blog post中介绍的方法不工作,因为它不完整。根据sqlalchemy documentation on joining sessions , Alex 的解决方案只有在没有回滚调用的情况下才有效。另一个区别是,与 Alex 博客上的作用域 session 相比,sqla 文档中使用了普通的 Session 对象。

对于 flask-sqlalchemy,作用域 session 在 request teardown 上自动删除.对 session.remove 进行调用,这会在后台发出回滚。要在测试范围内支持回滚,请使用 SAVEPOINT:

import sqlalchemy as sa


@pytest.yield_fixture(scope='function')
def db_session(db):
    """
    Creates a new database session for a test. Note you must use this fixture
    if your test connects to db.

    Here we not only support commit calls but also rollback calls in tests.
    """
    connection = db.engine.connect()
    transaction = connection.begin()

    options = dict(bind=connection, binds={})
    session = db.create_scoped_session(options=options)

    session.begin_nested()

    # session is actually a scoped_session
    # for the `after_transaction_end` event, we need a session instance to
    # listen for, hence the `session()` call
    @sa.event.listens_for(session(), 'after_transaction_end')
    def restart_savepoint(sess, trans):
        if trans.nested and not trans._parent.nested:
            session.expire_all()
            session.begin_nested()

    db.session = session

    yield session

    session.remove()
    transaction.rollback()
    connection.close()

不过,您的数据库必须支持 SAVEPOINT

关于python - 在 Flask-SQLAlchemy 中隔离 py.test 数据库 session ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28526781/

相关文章:

python - 这个 "score"到底是什么?使用 sklearn/Python 的额外树分类器

flask - 在 Gevent Socket-IO 中使用 Flask Session

python - chmod heroku 应用程序上的文件夹

python - 如何使用 url 中未显示的参数进行重定向?

node.js - 如何在Service构造函数中对Controller和模拟@InjectModel进行单元测试

python - Numpy 索引问题.....高级索引 X[0] 在这里做什么?

python - 给定边缘拆分 DataFrame 的最佳方法

Python:忽略 MYSQL 列名称的大小写敏感性

java - 创建单元测试用例时无法将 @Autowired 与 @Mock 一起使用

swift - iOS RxSwift - 使用 RxTest 和 TestScheduler 进行测试不会结束/终止测试用例