在Flask-SQLAlchemy中隔离py.test数据库会话 [英] Isolating py.test DB sessions in Flask-SQLAlchemy

查看:475
本文介绍了在Flask-SQLAlchemy中隔离py.test数据库会话的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

我编写了一个最小的完整示例来突出显示问题,注意 test_user_schema1() test_user_schema2()是相同的。 code> 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

如果db在我的测试之间是真正的隔离,那么这两个测试都应该通过。然而,最后一次测试总是失败,因为我还没有找到一个方法来使db会话正确回滚。





conftest.py 使用以下关于我在中看到的,但是这个夹具代码打破了,因为它显然不干'b
$ b

  @ 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


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

就这个问题而言,我建立了 a gist ,其中包含所有需要重现的内容;你可以用 git clone克隆https://gist.github.com/34fa8d274fc4be240933.git



I我使用下列软件包...

  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. 为什么现状被破坏?
  2. 我怎样才能解决这个问题?


解决方案


commit()用于提交当前事务。它总是事先发送flush()以将剩余的状态清除到数据库。这与自动刷新设置无关。 ....

所以 transaction.rollback()在会话夹具功能不会生效,因为交易已经提交。






2。



将夹具的范围更改为 function 而不是 session ,这样db每次被清除。 p>

  @ pytest.yield_fixture(scope ='function')
def app(request):
...
$ b $ pytest.yield_fixture(scope ='function')
def db(app,request):
...






顺便说一句,如果你使用内存中的sqlite数据库,你不需要删除数据库文件,它会更快:

pre $ DB $ ='sqlite://'#SQLite:内存:​​数据库


$ pytest.yield_fixture(scope ='function')
def db(app,request):
_db.app = app
_db .create_all()
yield _db
_db.drop_all()


I'm trying to build a Flask app with Flask-SQLAlchemy; I use pytest to test the DB. One of the problems seems to be creating isolated DB sessions between different tests.

I cooked up a minimal, complete example to highlight the problem, note that test_user_schema1() and test_user_schema2() are the same.

Filename: 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

If the db is truly isolated between my tests, both tests should pass. However, the last test always fails, because I haven't found a way to make db sessions rollback correctly.

conftest.py uses the following based on what I saw in Alex Michael's blog post, but this fixture code breaks because it apparently doesn't isolate the db sessions between fixtures.

@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()

For the purposes of this question, I built a gist, which contains all you need to reproduce it; you can clone it with git clone https://gist.github.com/34fa8d274fc4be240933.git.

I am using the following packages...

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

Two questions:

  1. Why is status quo broken? This same py.test fixture seemed to work for someone else.
  2. How can I fix this to work correctly?

解决方案

1.

According to Session Basics - SQLAlchemy documentation:

commit() is used to commit the current transaction. It always issues flush() beforehand to flush any remaining state to the database; this is independent of the "autoflush" setting. ....

So transaction.rollback() in session fixture function does not take effect, because the transaction is already committed.


2.

Change scope of fixtures to function instead of session so that db is cleared every time.

@pytest.yield_fixture(scope='function')
def app(request):
    ...

@pytest.yield_fixture(scope='function')
def db(app, request):
    ...


BTW, If you use in-memory sqlite database, you don't need to delete the db files, and it will be faster:

DB_URI = 'sqlite://'  # SQLite :memory: database

...

@pytest.yield_fixture(scope='function')
def db(app, request):
    _db.app = app
    _db.create_all()
    yield _db
    _db.drop_all()

这篇关于在Flask-SQLAlchemy中隔离py.test数据库会话的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆