Я пишу Flask приложение, которое использует SQLAlchemy для своей базы данных.
Приложение Flask создается с фабрикой приложений под названием create_app
.
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 для выполнения модульного тестирования.Я создаю pytest fixture с именем app_with_documents
, который вызывает фабрику приложений для создания приложения и добавляет некоторые объекты 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.)
@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 и /или SQLAlchemy?
- Как правильно написать тестовые приборы Flask, которые предварительно заполняют базу данных приложения?
Это Flask 1.0.2, Flask-SQLAlchemy 2.3.2 и pytest3.6.0, которые являются текущими последними версиями.