如何在Flask测试期间回滚数据库更改

时间:2017-10-29 11:46:21

标签: python flask sqlalchemy flask-sqlalchemy pytest

在pytest中运行测试时,会修改数据库。撤消对数据库的更改的最佳方法是什么?

DBSession回滚

对于那些我可以直接访问后端的测试,我目前使用pytest fixture为每个测试函数启动一个新的DBSession,并在结束时回滚会话

@pytest.fixture(scope='session')
def db(app, request):
    """Session-wide test database."""
    def teardown():
        pass

    _db = SQLAlchemy(app)    
    return _db

@pytest.fixture(scope='function')
def db_session(db, request):
    """Creates a new database session for a test."""
    engine = create_engine(
                            TestConfig.SQLALCHEMY_DATABASE_URI,
                            connect_args={"options": "-c timezone=utc"})
    DbSession = sessionmaker(bind=engine)
    session = DbSession()
    connection = engine.connect()
    transaction = connection.begin()
    options = dict(bind=connection, binds={})
    session = db.create_scoped_session(options=options)
    db.session = session

    yield session

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

在测试代码中,我只使用灯具

def test_create_project(db_session):     project = _create_test_project(db_session)     assert project.project_id> 0

Flask / HTTP测试

但是为了通过Flask / HTTP测试API,我无法使用db_session。即使我创建一个夹具来显式DROP测试数据库并从生产中恢复,它也无法工作,因为没有直接的数据库代码

@pytest.fixture(scope='function')
def db_session_refresh(db, request):
    """Refresh the test database from production after running the test"""
    engine = create_engine(
                            TestConfig.SQLALCHEMY_DATABASE_URI,
                            connect_args={"options": "-c timezone=utc"})
    DbSession = sessionmaker(bind=engine)
    session = DbSession()

    connection = engine.connect()
    transaction = connection.begin()

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

    db.session = session

    yield session

    transaction.rollback()
    connection.close()
    session.remove()  
    refresh_test_db_sql = """
        SELECT pg_terminate_backend(pid)
        FROM pg_stat_activity
        WHERE datname = 'appdb_test';

        DROP DATABASE appdb_test;

        CREATE DATABASE appdb_test TEMPLATE appdb;
        """
    engine.execute(refresh_test_db_sql)

即使这样可行,为每个函数刷新数据库也是低效的。

运行修改数据库的测试的正确/更好的方法是什么?

1 个答案:

答案 0 :(得分:0)

如前所述 - 数据库的创建和销毁应该从单元测试中解决并转移到包装器中。

但回答你的问题 - 我的印象是你正在使用postgresql。尝试从连接字符串中删除数据库名称并直接连接到该名称并将其添加到search_path