pytest-postgresql icon indicating copy to clipboard operation
pytest-postgresql copied to clipboard

Initializing the template database by way of fixtures

Open astoff opened this issue 1 year ago • 1 comments
trafficstars

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 avatar Mar 08 '24 07:03 astoff

@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

fizyk avatar Mar 08 '24 14:03 fizyk

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 avatar Mar 11 '24 09:03 astoff

@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.

fizyk avatar Mar 11 '24 10:03 fizyk

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.)

astoff avatar Mar 11 '24 10:03 astoff

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

fizyk avatar Mar 11 '24 13:03 fizyk

Yes, the linked PR should fix this. Thanks!

astoff avatar Mar 11 '24 13:03 astoff