-
Notifications
You must be signed in to change notification settings - Fork 89
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
pytest-flask example with SQLAlchemy #70
Comments
+1 Confusing docs. |
+1 |
I got something working, thanks to http://alexmic.net/flask-sqlalchemy-pytest/ Here's my code: import os
import pytest
from myapp import create_app, db as _db
@pytest.fixture(scope='session')
def app():
app = create_app()
app.config.from_object('test_settings')
return app
@pytest.fixture(scope='session')
def db(app, request):
if os.path.exists(app.config['DB_PATH']):
os.unlink(app.config['DB_PATH'])
def teardown():
_db.drop_all()
os.unlink(app.config['DB_PATH'])
_db.init_app(app)
_db.create_all()
request.addfinalizer(teardown)
return _db |
After much searching and hair-tearing, I found that the current approach works quite nicely: # module conftest.py
import pytest
from app import create_app
from app import db as _db
from sqlalchemy import event
from sqlalchemy.orm import sessionmaker
@pytest.fixture(scope="session")
def app(request):
"""
Returns session-wide application.
"""
return create_app("testing")
@pytest.fixture(scope="session")
def db(app, request):
"""
Returns session-wide initialised database.
"""
with app.app_context():
_db.drop_all()
_db.create_all()
@pytest.fixture(scope="function", autouse=True)
def session(app, db, request):
"""
Returns function-scoped session.
"""
with app.app_context():
conn = _db.engine.connect()
txn = conn.begin()
options = dict(bind=conn, binds={})
sess = _db.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
sess.remove()
# This instruction rollsback any commit that were executed in the tests.
txn.rollback()
conn.close() The key here is to run your tests within a nested session, and then rollback everything after the execution of each test (this also assumes there are no dependencies across your tests). |
@kenshiro-o it would be great if you could open a PR adding this to the docs somewhere. 😁 |
@nicoddemus sure! Will do so during the week 😸 |
Hi @kenshiro-o ,
I tried updating the factories during session creation and it actually works:
but I was wondering if some of you guys know a more "elegant" solution. |
@AnderUstarroz I encountered the same obstacle in the face factories not using the session, and overcome it with a bit more flexible solution:
|
This works with SQLAlchemy + Factoryboy: @pytest.fixture
def app():
app = make_app('test')
with app.app_context():
db.create_all()
yield app
db.session.remove()
db.drop_all() Then you may use |
Hi, there
Could you include a SQLAlchemy example in the documentation? I'm using the following code but it doesn't work:
This is the error that I get:
The text was updated successfully, but these errors were encountered: