python - flask +Pytest+SQLAlchemy : Can't create and drop tables when run pytest using flask-sqlalchemy

标签 python flask sqlalchemy pytest

当我运行测试时它成功连接到数据库,但它没有创建表。我想当我使用 flask-sqlalchemy 时可能有不同的创建表的方法,但我找不到解决方案。

这是app.py

db = SQLAlchemy()
def create_app(config_name):
    app = Flask(__name__, template_folder='templates')
    app.wsgi_app = ProxyFix(app.wsgi_app)
    app.config.from_object(config_name)
    app.register_blueprint(api)
    db.init_app(app)

    @app.route('/ping')
    def health_check():
        return jsonify(dict(ok='ok'))

    @app.errorhandler(404)
    def ignore_error(err):
        return jsonify()

    app.add_url_rule('/urls', view_func=Shorty.as_view('urls'))
    return app

这是run.py

environment = environ['TINY_ENV']
config = config_by_name[environment]
app = create_app(config)


if __name__ == '__main__':
    app.run()

这是config.py

import os

basedir = os.path.abspath(os.path.dirname(__file__))


class Config:
    """
    set Flask configuration vars
    """
    # General config
    DEBUG = True
    TESTING = False
    # Database
    SECRET_KEY = os.environ.get('SECRET_KEY', 'my_precious_secret_key')
    SQLALCHEMY_DATABASE_URI = 'mysql+pymysql://root@localhost:3306/tiny'
    SQLALCHEMY_TRACK_MODIFICATIONS = False
    SERVER_HOST = 'localhost'
    SERVER_PORT = '5000'


class TestConfig(Config):
    """
    config for test
    """
    TESTING = True
    SQLALCHEMY_DATABASE_URI = 'mysql+pymysql://root@localhost:3306/test_tiny'

config_by_name = dict(
    test=TestConfig,
    local=Config
)

key = Config.SECRET_KEY

这是模型.py

from datetime import datetime
from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()


class URLS(db.Model):
    __tablename__ = 'urls'

    id = db.Column(db.Integer, primary_key=True)
    original_url = db.Column(db.String(400), nullable=False)
    short_url = db.Column(db.String(200), nullable=False)
    created_at = db.Column(db.DateTime, default=datetime.utcnow()

这是测试配置设置。

db = SQLAlchemy()


@pytest.fixture(scope='session')
def app():
    test_config = config_by_name['test']
    app = create_app(test_config)

    app.app_context().push()
    return app


@pytest.fixture(scope='session')
def client(app):
    return app.test_client()


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

最佳答案

以下可能是阻止您的代码多次运行和/或阻止您删除/创建表的问题。不管它是否解决了您的问题,人们可能都没有意识到这一点,但要牢记这一点非常重要。 :)

当您多次运行测试时,db.drop_all() 可能不会被调用(因为您的一个测试失败了),因此,它可能无法在下次运行(因为它们已经存在)。问题在于使用没有 try:finally: 的上下文管理器。 (注意:每个使用 yield 的 fixture 都是上下文管理器)。

from contextlib import contextmanager

def test_foo(db):
    print('begin foo')
    raise RuntimeError()
    print('end foo')

@contextmanager
def get_db():
    print('before')
    yield 'DB object'
    print('after')

此代码代表您的代码,但不使用 pytest 的功能。 Pytest 或多或少像在运行它

try:
    with get_db(app) as db:
        test_foo(db)
except Exception as e:
    print('Test failed')

人们期望的输出类似于:

before
begin_foo
after
Test failed

但我们只得到

before
begin_foo
Test failed

当上下文管理器处于事件状态时(yield 已执行),我们的测试方法正在运行。如果在执行我们的测试函数期间引发异常,则执行将停止,而不会在 yield 语句之后运行任何代码。为了防止这种情况,我们必须将 fixture/contextmanager 包装在 try: ... finally: block 中。作为finally无论发生什么,总是执行。

@contextmanager
def get_db():
    print('before')
    try:
        yield 'DB object'
    finally:
        print('after')

yield 语句之后的代码现在按预期执行。

before
begin foo
after
Test failed

如果您想了解更多信息,请参阅 contextmanager docs 中的相关部分:

At the point where the generator yields, the block nested in the with statement is executed. The generator is then resumed after the block is exited. If an unhandled exception occurs in the block, it is reraised inside the generator at the point where the yield occurred. Thus, you can use a try…except…finally statement to trap the error (if any), or ensure that some cleanup takes place.

关于python - flask +Pytest+SQLAlchemy : Can't create and drop tables when run pytest using flask-sqlalchemy,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56939383/

相关文章:

python - Conda 安装一个已经存在的包

python - 来自 Python 字符流的 Lisp 分词器

python - 旋转 0 度的图像与原始图像相比具有不同的 base64

python - SQLAlchemy 自动映射数据并覆盖某些列

python - 在 SQLAlchemy 中对多列进行排序的语法

python - SQLAlchemy "WITH ORDINALITY"

python - 优化/删除循环

python - Nginx 提前切断静态文件下载

python - 嵌套 WTForms FieldList 在字段中生成 HTML

python - 在 Flask 应用程序中设置日期时间