我尝试使用pytest-postgresql执行一些测试,但似乎我没有完全掌握从fixture生成的引擎如何工作。下面的代码是我可以编写的最小示例,以重现我得到的错误。有两个测试分别以tokyo和伦敦命名,每个测试都使用DB连接创建一个以城市名称命名的模式,然后在模式中创建表citymeta
。
import pytest
from pytest_postgresql.janitor import DatabaseJanitor
from sqlalchemy import create_engine, inspect
from sqlalchemy.orm import Session
from sqlalchemy import text
from sqlalchemy import (Column, Integer, String)
from sqlalchemy.orm import declarative_base
Base = declarative_base()
class BaseModel(Base):
__abstract__ = True
id = Column(Integer, primary_key=True)
class CityMeta(BaseModel):
__tablename__ = "citymeta"
id = Column(Integer, primary_key=True)
version = Column(String(10), nullable=False)
def create_table(engine, schema):
with engine.connect() as conn:
conn.execute(text(f"""CREATE SCHEMA IF NOT EXISTS
{schema}"""))
conn.commit()
for table in BaseModel.metadata.tables.values():
table.schema = schema
table.create(engine, checkfirst=True)
schema1 = "tokyo"
schema2 = "london"
@pytest.fixture(scope="session")
def engine_postgresql(postgresql_proc):
with DatabaseJanitor(
postgresql_proc.user,
postgresql_proc.host,
postgresql_proc.port,
postgresql_proc.dbname,
postgresql_proc.version,
password=postgresql_proc.password,
):
yield create_engine(
f"postgresql+psycopg2://{postgresql_proc.user}:"
f"{postgresql_proc.password}@{postgresql_proc.host}:"
f"{postgresql_proc.port}/{postgresql_proc.dbname}"
)
def test_tokyo(engine_postgresql):
create_table(engine_postgresql, schema1)
insp = inspect(engine_postgresql)
assert insp.has_table("citymeta", schema=schema1)
with Session(engine_postgresql) as session:
meta = CityMeta(id=1, version='2.3')
session.add(meta)
session.commit()
res = session.query(CityMeta).filter_by(id=1).first()
assert res.version == '2.3'
def test_london(engine_postgresql):
create_table(engine_postgresql, schema2)
insp = inspect(engine_postgresql)
assert insp.has_table("citymeta", schema=schema2)
with Session(engine_postgresql) as session:
meta = CityMeta(id=1, version='0.0')
session.add(meta)
session.commit()
res = session.query(CityMeta).filter_by(id=1).first()
assert res.version == '0.0'
因为每个测试都创建了一个单独的模式,所以我希望这两个测试都能正常运行。但是,我在第二次测试中得到以下错误:
sqlalchemy.exc.IntegrityError: (psycopg2.errors.UniqueViolation) duplicate key value violates unique constraint "citymeta_pkey"
E DETAIL: Key (id)=(1) already exists.
E
E [SQL: INSERT INTO tokyo.citymeta (id, version) VALUES (%(id)s, %(version)s)]
E [parameters: {'id': 1, 'version': '0.0'}]
E (Background on this error at: https://sqlalche.me/e/20/gkpj)
看起来在第二个测试(伦敦)中,即使london模式成功创建,当会话启动时,它仍然使用模式tokyo,并且由于密钥冲突(两个id都是1)而无法插入数据。我知道pytest-postgresql在测试结束前维护着同一个数据库,但是,由于在第二个测试中生成了一个新的模式,新的会话不应该使用这个新的模式吗?
1条答案
按热度按时间dz6r00yl1#
受到this question的答案的启发,我在第二个测试中添加了这行代码来运行测试: