pytest-postgresql
pytest-postgresql copied to clipboard
Initializing the template database by way of fixtures
What action do you want to perform
It would be nice to be able to initialize the template database using session-level fixtures. In this way, it's easy to share some data (say some random UUIDs) between the initialization process and the actual tests.
AFAICS this is not easily done with the load argument of the template DB fixtures.
What are the results
If do not provide a load argument to the template DB and instead define a session-level fixture that requests the template DB and populates it, I get:
asyncpg.exceptions.ObjectNotInPrerequisiteStateError: database "tests_tmpl" is not currently accepting connections
What are the expected results
I would expect to be able to modify the template DB within a session-level fixture.
@astoff
Being able to modify the template database in a separate fixture would be.... tricky. Could you describe to me your use case? From the process fixture up to the tests?
As for accepting connections - #914
Suppose I have
class Person(DeclarativeBase):
__tablename__ = "person"
id: Mapped[UUID] = mapped_column(primary_key=True, default=uuid4)
name: Mapped[str]
and suppose I want to have my test DB include an example person.
Currently, the only option is:
def initialize(**kwargs):
create_database_schema(**kwargs)
session = somehow_obtain_a_db_session(**kwargs)
session.add(Person(name="Johnny")
session.commit()
postgresql_my_proc = pytest_postgresql.factories.postgresql_proc(load=[initialize], ...)
postgresql_my = factories.postgresql('postgresql_my_proc'))
def test_person(postgresql_my):
johnny_id = ???
That is, inside test_person, how do I know the random UUID of the person "Johnny"?
So I would like to be able to say this:
def initialize(**kwargs):
create_database_schema(**kwargs)
postgresql_my_proc = pytest_postgresql.factories.postgresql_proc(load=[initialize], ...)
@pytest.fixture(scope="session")
def johnny_id(postgresql_my_proc):
session = somehow_obtain_a_db_session(**kwargs)
johnny = Person(name="Johnny")
session.add(johnny)
session.commit()
return johnny.id
postgresql_my = factories.postgresql('postgresql_my_proc'))
def test_person(postgresql_my, johnny_id):
session = somehow_obtain_a_db_session(postgresql_my)
johnny = session.scalar(select(Person).where(Person.id == johnny_id))
assert johnny.name = "Johnny"
@astoff if you want to check the uuid4 functionality, then you should assign a Unique name for the Person, if that's not important, then you should assign a known uuid4 by hand. (ie keep it in a test constant)
However, this example rather suggests that the data is a test fixture, not necessarily one that should land in the template database.
Yes, it's true that the test data could be a normal (function-scoped) fixture. But everything else could as well, right? I mean, the only reason to have a template db is to speed up the tests. If creating "Johnny" is slow, it would be much better to be able to do it as a session-level fixture, like the template DB itself. (The other option you mention, to create UUIDs by hand, would work, but I find it too messy.)
Introducing #914 might allow you to do what you want, but... I'd not advise it. Having a fixture in between the process fixture and the test itself, that will add data to the template database might lead to unexpected, non-deterministic results, especially if you have some tests using the intermediary fixture and some without it. It would just escalate its values upwards.
From what I recall, the test order is not guaranteed and might be especially tricky if you also run tests with Xdist
Yes, the linked PR should fix this. Thanks!