Pagination driver¶
drive_paginationfollowsnextlinks to exhaustion, accumulates the items, and checks the envelope invariants on every page — so a pagination test is one call.
Verifying pagination by hand means a loop that follows next, accumulates items,
and re-checks the envelope each time — easy to write subtly wrong, and easy to hang
on a server whose next never terminates. drive_pagination is that loop, done
once and correctly. It is deferred-link aware: it reads gazebo's already-resolved
link hrefs straight from the response, the same ones a real client would follow.
from gazebo.testing import drive_pagination
# Follow every `next` link, accumulating items to exhaustion. On each page it
# asserts the envelope invariants — numberReturned == len(items), and (with
# limit=) no page over the limit — and guards against a looping `next`.
all_numbers = drive_pagination(client, '/numbers', items_key='items', limit=2)
assert all_numbers == [0, 1, 2, 3, 4]
What it checks¶
On every page (not just the final total) it asserts:
- the page contains the
items_keyyou named; - if the body has
numberReturned, it equals the number of items on that page; - if you pass
limit=, no page exceeds it.
It also guards against a runaway or looping next: revisiting a request it has
already made raises immediately (rather than hanging), and max_pages is a hard
backstop. It returns the accumulated items, so the content assertions (ordering,
the full set) stay in your test rather than being baked into the driver.
The client¶
The first argument is any object with a request(method, url, json=...) method — a
Starlette/httpx TestClient. For an authenticated service, pass request_kwargs;
it is forwarded to every request, so you hand the TestClient in directly rather
than wrapping it:
drive_pagination(client, '/plants', items_key='plants',
request_kwargs={'headers': {'authorization': 'Bearer ...'}})
GET and POST pagination¶
By default it issues GET requests and follows the next link's href. For
POST-driven pagination (as in STAPI), pass method='POST' and an initial body; a
body member on the next link is then carried into the following request. Because
POST pagination legitimately reposts the same URL with a different body, the loop
guard keys on the body too in that mode, so it won't mistake progress for a loop.
Other knobs: rel= follows a relation other than next; max_pages= caps the
walk.
Reference¶
See gazebo.testing.