简体   繁体   English

在集成测试 (Pytest) 和 Flask 应用程序之间创建不同的 SQLAlchemy 会话

[英]Create distinct SQLAlchemy sessions between integration test (Pytest) and Flask application

I have flask application (RESTful API backend) that uses flask-sqlalchemy lib.我有使用flask-sqlalchemy lib的flask应用程序(RESTful API后端)。 The integration test uses Pytest with fixtures, some create records for test purposes.集成测试使用带有夹具的 Pytest,一些创建用于测试目的的记录。 The problem is that when testing scenario that's expected to raise unique constraint failure at the database level, the records created for test via fixtures all got rolled back, causing other tests to fail with error sqlalchemy.exc.InvalidRequestError: This Session's transaction has been rolled back due to a previous exception during flush. To begin a new transaction with this Session, first issue Session.rollback(). Original exception was: (sqlite3.IntegrityError) UNIQUE constraint failed: my_table.field1问题是,当测试预期在数据库级别引发唯一约束失败的场景时,通过夹具为测试创建的记录全部回滚,导致其他测试失败,错误为sqlalchemy.exc.InvalidRequestError: This Session's transaction has been rolled back due to a previous exception during flush. To begin a new transaction with this Session, first issue Session.rollback(). Original exception was: (sqlite3.IntegrityError) UNIQUE constraint failed: my_table.field1 sqlalchemy.exc.InvalidRequestError: This Session's transaction has been rolled back due to a previous exception during flush. To begin a new transaction with this Session, first issue Session.rollback(). Original exception was: (sqlite3.IntegrityError) UNIQUE constraint failed: my_table.field1 sqlalchemy.exc.InvalidRequestError: This Session's transaction has been rolled back due to a previous exception during flush. To begin a new transaction with this Session, first issue Session.rollback(). Original exception was: (sqlite3.IntegrityError) UNIQUE constraint failed: my_table.field1 . sqlalchemy.exc.InvalidRequestError: This Session's transaction has been rolled back due to a previous exception during flush. To begin a new transaction with this Session, first issue Session.rollback(). Original exception was: (sqlite3.IntegrityError) UNIQUE constraint failed: my_table.field1

How do I go about creating distinct SQLAlchemy session for testing so that test records can be committed to DB and not impacted by errors that happen inside Flask request lifecycle?我如何着手创建不同的 SQLAlchemy 会话进行测试,以便测试记录可以提交到数据库,而不受 Flask 请求生命周期内发生的错误的影响?

### globals.py ###

from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()
### app.py ###

from sqlalchemy.exc import IntegrityError
from .globals import db

def handle_unique_constraint(error):
    return {"msg": "Duplicate"}, 409

def create_app(connection_string):
    app = Flask(__name__)
    app.register_error_handler(IntegrityError, handle_unique_constraint)
    app.config['SQLALCHEMY_DATABASE_URI'] = connection_string

    # register API blueprint ...
    # e.g. create new user record, done like this:
    #
    # db.session.add(User(**{'email': path_param}))
    # db.session.commit()

    db.init_app(app)
    return app
### conftest.py ###

from my_package.app import create_app
from my_package.globals import db as app_db
from my_package.models.user import User

@fixture(scope='session')
def app(request):
    app = create_app('https://localhost')
    app.debug = True

    with app.app_context():
        yield app

@fixture(scope='session')
def db(app):
    app_db.create_all()
    return app_db

@fixture(scope='session')
def client(app):

    with app.test_client() as client:
        yield client

@fixture(scope='function')
def test_user(db):
    user = User(**{'email': generate_random()})
    db.session.add(user)
    db.session.commit()
    db.session.refresh(user)
### test_user.py ###



# This test passses

def test_repeat_email(client, test_user):
    resp = client.post('/users/emails/{}'.format(test_user.email))
    assert resp.status_code == 409


# This test errors out during setting up test_user fixture
# with aforementioned error message

def test_good_email(client, test_user): # <- this 
    resp = client.post('/users/emails/{}'.format('unique@example.com'))
    assert resp.status_code == 201

You have to implement a setUp and a tearDown .你必须实现一个setUp和一个tearDown

When running a tests the setUp will be run at the start of each test.运行测试时, setUp将在每个测试开始时运行。 The tearDown will be run at the end of each one. tearDown将在每个结束时运行。

In the setUp you will: initialize the databasesetUp你会:初始化数据库

In the tearnDown you will: close db connections, remove items created ...tearnDown您将:关闭数据库连接,删除创建的项目...


I'm not familiar to pytest .我对pytest不熟悉。 According to this and this .根据这个这个

Before the yield it's the setUp part and after it's the tearDown .yield之前是setUp部分,之后是tearDown

You should have something like this :你应该有这样的东西:

@fixture(scope='session')
def app(request):
    app = create_app('https://localhost')
    app.debug = True
    
    ctx = app.app_context()
    ctx.push()

    yield app

    ctx.pop()


@fixture(scope='session')
def db(app):
    app_db.create_all()
    yield app_db
    app_db.drop_all()

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM