【问题标题】:flask_sqlalchemy create_all() fails silently in unit testingflask_sqlalchemy create_all() 在单元测试中静默失败
【发布时间】:2017-06-14 18:10:50
【问题描述】:

我正在为 Flask 中使用 flask_sqlalchemy 扩展编写的 REST API 编写单元测试。因为我有多个模型类,所以我写了一个TestCase子类来做测试数据库的标准setUp/cleanUp。我所有的测试类都继承自这个。每个测试在单独运行时都会成功,但是当我在一个类中运行多个测试时,第二个 setUp() 在 self.db.session.commit() 上失败(我正在尝试向 User 表添加一个条目),因为 self.db.create_all() 有(默默地)未能创建任何表。 这是我的基础测试类,在测试包的__init__.py 中:

import unittest

from .test_client import TestClient
from .. import create_app
from pdb import set_trace as DBG

class ApiTest(unittest.TestCase):
    default_username = 'fred'
    default_password = 'bloggs'
    db = None

    def setUp(self):
        try:
            self.app = create_app('testing')
            self.addCleanup(self.cleanUp)
            self.ctx = self.app.app_context()
            self.ctx.push()
            from .. import db
            self.db = db
            self.db.session.commit()
            self.db.drop_all(app=self.app)
            from ..models import User, Player, Team, Match, Game
            # self.app.logger.debug('drop_all())')
            self.db.create_all(app=self.app)
            # self.app.logger.debug('create_all())')
            user = User(user_name=self.default_username)
            user.password = self.default_password
            self.db.session.add(u)
            self.db.session.commit()
            self.client = TestClient(self.app, user.generate_auth_token(), '')
        except Exception, ex:
            self.app.logger.error("Error during setUp: %s" % ex)
            raise

    def cleanUp(self):
        try:
            self.db.session.commit()
            self.db.session.remove()
            self.db.drop_all(app=self.app)
            # self.app.logger.debug('drop_all())')
            self.ctx.pop()
        except Exception, ex:
            self.app.logger.error("Error during cleanUp: %s" % ex)
            raise

谁能告诉我这里出了什么问题?

编辑:按要求添加了create_app() 的代码。

# chessleague/__init__.py
import os

from flask import Flask, g
from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager


from . import config


app = None
db = None  # The database, initialised in create_app()


def create_app(config_name):
    app = Flask(__name__)
    app.config.update(config.get_config(config_name))
    # if app.config['USE_TOKEN_AUTH']:
    #     from api.token import token as token_blueprint
    #     app.register_blueprint(token_blueprint, url_prefix='/auth')
    import logging
    from logging.handlers import SysLogHandler
    syslog_handler = SysLogHandler()
    syslog_handler.setLevel(logging.WARNING)
    app.logger.addHandler(syslog_handler)

    login_manager = LoginManager()
    login_manager.login_view = 'auth.login'
    login_manager.init_app(app)
    global db
    db = SQLAlchemy(app)
    db.init_app(app)
    from .models import User,Player,Game,Match,Team,Post
    db.create_all()
    from .api import api as api_blueprint
    app.register_blueprint(api_blueprint, url_prefix='/chessleague')
    return app

`

【问题讨论】:

  • 您提到了create_all 的问题,但没有添加它是如何调用的。我想它是在“create_app”中调用的,你能把这个函数添加到你的问题中让我们看看吗?
  • db.create_all() 确实在上面的 setUp() 方法中被调用,但您的问题让我再次查看主包中的 create_app()。事实证明,它也调用了db.create_all(),正如您所猜测的那样。我已将其添加到问题中。
  • ... 但 AFAIK create_all() 应该是幂等的。
  • 有一个错字。我一直对create_app(不是create_all)功能感兴趣。如果您的models.py 导入一个数据库,或在其内部创建实例,您应该尝试将create_app() 中的行更改为from .models import User,Player,Game,Match,Team,Post, db as models_dbmodels_db.create_all(),这样您将使用models.py 中的数据库创建表
  • 你成功了!问题在于appdb 的初始化顺序。我从基础包中导入的db(并调用create_all())实际上并不是绑定到模型的那个。因此create_all() 什么也没创造。

标签: python unit-testing flask flask-sqlalchemy


【解决方案1】:

create_all() 适用于元数据,这是通过导入带有模型的模块来发现的。在您的情况下,模型的元数据从您的models.py 绑定到db,但您从chessleague/__init__.db 调用create_all()create_app() 函数,这是SqlAlchemy 的不同对象。您可以使用来自models.pydb 来解决这个问题:

from .models import User,Player,Game,Match,Team,Post, db as models_db
models_db.create_all()

【讨论】:

    【解决方案2】:

    这是对我有用的初始化序列 - 欢迎 cmets!

    我的测试类 setUp() 从主应用程序包中调用 create_app(config_name)。 主应用程序包(__init__.py)在模块级别创建应用程序实例,即app=Flask(my_app_package_name) 然后我的功能 create_app(config_name)

    • 将正确的配置加载到 app.config 中(包括正确的 SQLACHEMY_DATABASE_URL)
    • 从models.py导入模型类和db(如model_db

    此导入在 models.py 中的模块级别创建符号 db,然后是模型类定义:

      # models.py
        from . import app
        db = SQLAlchemy(app)
        ...
        class User(db.Model)
        ... 
        etc
    

    现在一切都已正确设置:符号“db”可以从 models.py 中的任何位置导入,我可以从我的测试 setUp() 中成功调用 db.create_all()

    @Fian,您能否发布您的解决方案作为答案,以便我给予您信任?

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-07-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-02-22
      • 2018-11-25
      相关资源
      最近更新 更多