我正在编写一个Flask应用程序,它使用SQLAlchemy作为其数据库后端。
使用名为create_app
的应用工厂创建Flask应用程序。
from flask import Flask
def create_app(config_filename = None):
app = Flask(__name__)
if config_filename is None:
app.config.from_pyfile('config.py', silent=True)
else:
app.config.from_mapping(config_filename)
from .model import db
db.init_app(app)
db.create_all(app=app)
return app
数据库模型由一个名为Document
的对象组成。
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
class Document(db.Model):
id = db.Column(db.Integer, primary_key=True)
document_uri = db.Column(db.String, nullable=False, unique=True)
我正在使用pytest进行单元测试。我创建了一个名为app_with_documents
的pytest fixture,它调用应用程序工厂来创建一个应用程序,并在运行测试之前将一些Document
对象添加到数据库中,然后在单元之后清空数据库测试已经完成。
import pytest
from model import Document, db
from myapplication import create_app
@pytest.fixture
def app():
config = {
'SQLALCHEMY_DATABASE_URI': f"sqlite:///:memory:",
'TESTING': True,
'SQLALCHEMY_TRACK_MODIFICATIONS': False
}
app = create_app(config)
yield app
with app.app_context():
db.drop_all()
@pytest.fixture
def app_with_documents(app):
with app.app_context():
document_1 = Document(document_uri='Document 1')
document_2 = Document(document_uri='Document 2')
document_3 = Document(document_uri='Document 3')
document_4 = Document(document_uri='Document 4')
db.session.add_all([document_1, document_2, document_3, document_4])
db.session.commit()
return app
我有多个使用此灯具的单元测试。
def test_unit_test_1(app_with_documents):
...
def test_unit_test_2(app_with_documents):
...
如果我运行单个单元测试,一切正常。如果我运行多个测试,后续的单元测试会在测试夹具设置的db.session.commit()
行崩溃,并且“没有这样的表:文档”。
def do_execute(self, cursor, statement, parameters, context=None):
> cursor.execute(statement, parameters)
E sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) no such table: document [SQL: 'INSERT INTO document (document_uri) VALUES (?)'] [parameters: ('Document 1',)] (Background on this error at: http://sqlalche.me/e/e3q8)
我期望每个单元测试都有自己全新的相同预填充数据库,以便所有测试都能成功。
(这是数据库表的问题,而不是单元测试。即使我的单元测试只包含pass
,我也会看到错误。)
错误消息提到缺少表的事实使得在第一次单元测试运行后,db.create_all(app=app)
中的create_app
看起来没有被调用。但是,我已在调试器中验证了此应用程序工厂函数是否按预期为每个单元测试调用一次。
我对db.drop_all()
的调用可能是清除数据库的错误方法。因此,我尝试在磁盘上创建一个,然后将其作为测试夹具清理的一部分删除,而不是内存数据库。 (这是Flask文档中的技术recommended。)
@pytest.fixture
def app():
db_fd, db_filename = tempfile.mkstemp(suffix='.sqlite')
config = {
'SQLALCHEMY_DATABASE_URI': f"sqlite:///{db_filename}",
'TESTING': True,
'SQLALCHEMY_TRACK_MODIFICATIONS': False
}
yield create_app(config)
os.close(db_fd)
os.unlink(db_filename)
这会产生同样的错误。
这是Flask 1.0.2,Flask-SQLAlchemy 2.3.2和pytest 3.6.0,它们都是当前最新版本。
答案 0 :(得分:0)
在我的conftest.py
我正在我的应用程序中导入model.py
的内容。
from model import Document, db
我使用Pycharm的pytest runner在Pycharm中运行单元测试。相反,如果我使用python -m pytest
从命令行运行测试,我会看到以下错误
ModuleNotFoundError: No module named 'model'
ERROR: could not load /Users/wmcneill/src/FlaskRestPlus/test/conftest.py
我可以通过在conftest.py
中完全限定导入路径来从命令行运行我的测试。
from myapplication.model import Document, db
当我这样做时,所有的单元测试都通过了。当我从Pycharm内部运行单元测试时,它们也会通过。
所以看来我在单元测试中错误地写了一个import语句。但是,当我通过Pycharm运行这些单元测试时,不是看到有关导入的错误消息,而是启动了脚本,但后来出现了奇怪的SQL错误。
我还没弄清楚为什么我看到了我所做的奇怪的SQL错误。大概是关于全球国家处理方式的微妙之处。但是更改导入行可以解决我的问题。