Repository navigation
pytest-queryguard: ORM-level N+1 detection for SQLAlchemy 2 tests #15166
Replies: 2 comments
|
I'd keep fixture setup out of the default. Data seeding can do a lot of intentional database work, and a session-scoped fixture would otherwise charge its setup to whichever test happens to run first. For a fixture that is the code being tested, the current One detail: lazy relationship accesses after the I checked this against current main ( |
|
An update, since a lot has changed since I opened this: 1.0.0rc1 is on PyPI now. The public API (markers, options, report format) is frozen, and I'd like feedback from pytest users before 1.0.0, which I plan to release around 2026-11-18. A test database usually has a handful of rows, so a test like this passes even when def test_list_orders(session):
assert len(list_orders(session)) == 50With the plugin installed it fails, and the failure section says where the loads happen and how to fix them: @Muguett-DBY, thanks for the answer on fixtures. That's how it ended up: queries from the test's own code (fixtures, factories, What it doesn't do: it only sees what your tests execute, not production traffic, and it doesn't rewrite queries for you. Short runs of 2 to 4 identical statements are not reported by default. There is Django ORM support now, but it is experimental and may still change after 1.0. The things I'd most like opinions on, as the person who maintains it and has mostly heard from SQLAlchemy users so far: the marker names ( Docs: https://maciejziel.github.io/pytest-queryguard/ |
Uh oh!
There was an error while loading. Please reload this page.
Hi! I've been working on pytest-queryguard and could use some feedback from other pytest users.
It catches N+1 queries in SQLAlchemy 2 and fails the test. In #14344, @RonnyPfannschmidt mentioned wanting this at the ORM level. That's what I'm trying here.
I didn't want to require query-count assertions for each test. Instead, I listen to
do_orm_execute. SQLAlchemy tells me which relationship is being lazy-loaded and for which parent. The test fails once two different parents lazy-load the same relationship. There are no expected counts to update.If you want a query limit, you can add
@pytest.mark.max_queries(n). It's optional. You can use sync or async sessions, and FastAPI'sTestClientworks too. I've also added a missing-index check for PostgreSQL 16+. That check is off unless you enable it.It's on PyPI, and the code is on GitHub. I also wrote a post about how it works.
I'm still unsure where to draw the line with fixtures. Right now I only watch the test body, using a
pytest_runtest_callwrapper. I leave fixture setup out. Would you want me to count queries from fixtures too?There are also two cases I don't catch. If the identity map handles a lazy load, there's no SQL for me to see. I also miss eager loads that never get used. Have you tried tracking either of these? I'd be interested to hear what worked.
All reactions