2015-12-04 83 views
5

我正在努力編寫py.test fixtures來管理我的應用程序的數據庫,以最大限度地提高速度,支持pytest-xdist並行化的測試,並隔離測試彼此。如何將py.test裝置與Flask-SQLAlchemy和PostgreSQL結合使用?

我正在使用Flask-SQLAlchemy 2.1針對PostgreSQL 9.4數據庫。

這裏是什麼,我試圖完成的大致輪廓:

  1. $ py.test -n 3旋轉起來運行測試三個測試會議。

  2. 在每個會話中,py.test夾具運行一次以建立事務,創建數據庫表,然後在會話結束時它回滾事務。創建數據庫表需要在只對特定測試會話可見的PostgreSQL事務內發生,否則由pytest-xdist創建的並行化測試會話會相互衝突。

  3. 爲每個測試運行的第二個py.test夾具連接到現有事務,以便查看創建的表,創建嵌套保存點,運行測試,然後回滾到嵌套保存點。

  4. 理想情況下,這些pytest夾具支持調用db.session.rollback()的測試。在此SQLAlchemy doc的底部有一個可能的配方來完成此操作。

  5. 理想的pytest燈具應產生db對象,而不僅僅是會話,以便 人可以編寫測試,而無需記住使用一個會話這 比他們在整個應用程序中使用的標準不同db.session

這是我到目前爲止有:

import pytest 

# create_app() is my Flask application factory 
# db is just 'db = SQLAlchemy()' + 'db.init_app(app)' within the create_app() function 
from app import create_app, db as _db 


@pytest.yield_fixture(scope='session', autouse=True) 
def app(): 
    '''Session-wide test application''' 
    a = create_app('testing') 
    with a.app_context(): 
     yield a 

@pytest.yield_fixture(scope='session') 
def db_tables(app): 
    '''Session-wide test database''' 
    connection = _db.engine.connect() 
    trans = connection.begin() # begin a non-ORM transaction 

    # Theoretically this creates the tables within the transaction 
    _db.create_all() 
    yield _db 
    trans.rollback() 
    connection.close() 

@pytest.yield_fixture(scope='function') 
def db(db_tables): 
    '''db session that is joined to existing transaction''' 

    # I am quite sure this is broken, but it's the general idea 

    # bind an individual Session to the existing transaction 
    db_tables.session = db_tables.Session(bind=db_tables.connection) 

    # start the session in a SAVEPOINT... 
    db_tables.session.begin_nested() 

    # yield the db object, not just the session so that tests 
    # can be written transparently using the db object 
    # without requiring someone to understand the intricacies of these 
    # py.test fixtures or having to remember when to use a session that's 
    # different than db.session 
    yield db_tables 

    # rollback to the savepoint before the test ran 
    db_tables.session.rollback() 
    db_tables.session.remove() # not sure this is needed 

這裏的,而谷歌搜索,我已經找到了最有用的參考資料:

http://docs.sqlalchemy.org/en/latest/orm/session_transaction.html#joining-a-session-into-an-external-transaction-such-as-for-test-suites

http://koo.fi/blog/2015/10/22/flask-sqlalchemy-and-postgresql-unit-testing-with-transaction-savepoints/

https://github.com/mitsuhiko/flask-sqlalchemy/pull/249

回答

0

我有類似的問題,試圖結合yield燈具。不幸的是,根據doc,您無法合併多個yield級別。

但是,你也許能找到一個變通使用request.finalizer

@pytest.fixture(scope='session', autouse=True) 
def app(): 
    '''Session-wide test application''' 
    a = create_app('testing') 
    with a.app_context(): 
     return a 

@pytest.fixture(scope='session') 
def db_tables(request, app): 
    '''Session-wide test database''' 
    connection = _db.engine.connect() 
    trans = connection.begin() # begin a non-ORM transaction 

    # Theoretically this creates the tables within the transaction 
    _db.create_all() 
    def close_db_session(): 
     trans.rollback() 
     connection.close() 
    request.addfinalizer(close_db_session) 
    return _db 
相關問題