使用 SqlAlchemy 为单个测试函数重新创建数据库失败
Recreating a database using SqlAlchemy for individual test functions fails
我正在努力更新我对 Flask 和 SqlAlchemy 的了解。
我完成了教程 (https://flask.palletsprojects.com/en/2.0.x/tutorial/index.html)
现在我正在调整它以包括 SqlAlchemy。我尽量不使用 Flask-SqlAlchemy 插件,假设这会让我更好地理解事情。
但是,我 运行遇到了测试问题。在原始教程中,每个测试函数都创建了一个新数据库。在开始每个测试之前产生一个状态已知的干净数据库。
现在我想对 SqlAlchemy 做同样的事情但失败了。请参见下面的代码示例:
定义了两个测试(test_create_app
和 test_create_another_app
)。当我 运行 他们分别通过。当我 运行 他们在一个会话中时,第二个失败。
我可以确认这两个测试都有自己的数据库文件。还可以创建 table 作品。但是我可以看到第二个 table 没有任何用户,而我确实创建了它们。
我是否在尝试做一些我不应该做的事情?或者我在这里做错了什么?
如果我查看有关测试数据库的 FastAPI 文档 (https://fastapi.tiangolo.com/advanced/testing-database/),每个会话只创建一个数据库。我想以某种其他方式管理单个测试的状态。
感谢任何帮助!
import sqlalchemy as sa
from sqlalchemy import create_engine, orm
from sqlalchemy.sql.expression import select
import pytest
session_factory = None
# ~~~~~~~~~~~~~~~~~~~~~ ORM models
SqlAlchemyBase = sa.ext.declarative.declarative_base()
class User(SqlAlchemyBase):
__tablename__ = "user"
id: int = sa.Column(sa.Integer, primary_key=True, autoincrement=True)
username = sa.Column(sa.String, unique=True, nullable=False)
password = sa.Column(sa.String, nullable=False)
USERS = [
User(username="Güllich", password="action direct"),
User(username="Graham", password="the island"),
]
# ~~~~~~~~~~~~~~~~~~~~~ Database management
def init_db(connection_url: str):
"""Initialize the database."""
global session_factory
if session_factory:
raise Exception("you should run this function only once.")
engine = create_engine(connection_url, future=True)
session_factory = orm.sessionmaker(bind=engine)
SqlAlchemyBase.metadata.create_all(bind=engine)
def close_db():
"""Close the database"""
global session_factory
if session_factory:
session_factory.close_all()
session_factory = None
# ~~~~~~~~~~~~~~~~~~~~~ Testing part.
@pytest.fixture
def dbase(tmp_path):
sqlite_file = tmp_path / "test_db.sqlite"
connection_string = f"sqlite+pysqlite:///{sqlite_file.as_posix()}"
print("creating database at: %s", connection_string)
init_db(connection_string)
session = session_factory()
for user in USERS:
session.add(user)
session.commit()
session.close()
yield
close_db()
def test_create_app(dbase):
db_session = session_factory()
query = select(User)
users = db_session.execute(query).scalars().all()
assert len(users) == 2
def test_create_another_app(dbase):
db_session = session_factory()
query = select(User)
users = db_session.execute(query).scalars().all()
assert len(users) == 2
我发现我上面的代码有什么问题:
sqlalchemy orm 实例的 USERS 列表 - 我想显然 - 只能绑定到数据库一次。所以第二次尝试这样做会失败(不幸的是默默地)。
我正在努力更新我对 Flask 和 SqlAlchemy 的了解。 我完成了教程 (https://flask.palletsprojects.com/en/2.0.x/tutorial/index.html) 现在我正在调整它以包括 SqlAlchemy。我尽量不使用 Flask-SqlAlchemy 插件,假设这会让我更好地理解事情。
但是,我 运行遇到了测试问题。在原始教程中,每个测试函数都创建了一个新数据库。在开始每个测试之前产生一个状态已知的干净数据库。
现在我想对 SqlAlchemy 做同样的事情但失败了。请参见下面的代码示例:
定义了两个测试(test_create_app
和 test_create_another_app
)。当我 运行 他们分别通过。当我 运行 他们在一个会话中时,第二个失败。
我可以确认这两个测试都有自己的数据库文件。还可以创建 table 作品。但是我可以看到第二个 table 没有任何用户,而我确实创建了它们。
我是否在尝试做一些我不应该做的事情?或者我在这里做错了什么? 如果我查看有关测试数据库的 FastAPI 文档 (https://fastapi.tiangolo.com/advanced/testing-database/),每个会话只创建一个数据库。我想以某种其他方式管理单个测试的状态。
感谢任何帮助!
import sqlalchemy as sa
from sqlalchemy import create_engine, orm
from sqlalchemy.sql.expression import select
import pytest
session_factory = None
# ~~~~~~~~~~~~~~~~~~~~~ ORM models
SqlAlchemyBase = sa.ext.declarative.declarative_base()
class User(SqlAlchemyBase):
__tablename__ = "user"
id: int = sa.Column(sa.Integer, primary_key=True, autoincrement=True)
username = sa.Column(sa.String, unique=True, nullable=False)
password = sa.Column(sa.String, nullable=False)
USERS = [
User(username="Güllich", password="action direct"),
User(username="Graham", password="the island"),
]
# ~~~~~~~~~~~~~~~~~~~~~ Database management
def init_db(connection_url: str):
"""Initialize the database."""
global session_factory
if session_factory:
raise Exception("you should run this function only once.")
engine = create_engine(connection_url, future=True)
session_factory = orm.sessionmaker(bind=engine)
SqlAlchemyBase.metadata.create_all(bind=engine)
def close_db():
"""Close the database"""
global session_factory
if session_factory:
session_factory.close_all()
session_factory = None
# ~~~~~~~~~~~~~~~~~~~~~ Testing part.
@pytest.fixture
def dbase(tmp_path):
sqlite_file = tmp_path / "test_db.sqlite"
connection_string = f"sqlite+pysqlite:///{sqlite_file.as_posix()}"
print("creating database at: %s", connection_string)
init_db(connection_string)
session = session_factory()
for user in USERS:
session.add(user)
session.commit()
session.close()
yield
close_db()
def test_create_app(dbase):
db_session = session_factory()
query = select(User)
users = db_session.execute(query).scalars().all()
assert len(users) == 2
def test_create_another_app(dbase):
db_session = session_factory()
query = select(User)
users = db_session.execute(query).scalars().all()
assert len(users) == 2
我发现我上面的代码有什么问题:
sqlalchemy orm 实例的 USERS 列表 - 我想显然 - 只能绑定到数据库一次。所以第二次尝试这样做会失败(不幸的是默默地)。