Pagination Testing Best Practices (2026)
Pagination Testing Best Practices (2026) begins with recognizing that pagination is a contract, not just a UI component. Teams that treat it as a mere visual flourish often miss subtle data‑integrity
Pagination Testing Best Practices (2026) begins with recognizing that pagination is a contract, not just a UI component. Teams that treat it as a mere visual flourish often miss subtle data‑integrity bugs, and accessibility issues that surface only under real‑world load. This guide walks you through the principles, a prioritized checklist, what to automate versus test manually, the failure modes that repeatedly appear in production, metrics that matter, tooling choices, CI/CD integration, and the anti‑patterns to avoid. Concrete examples, two comparison tables, and code snippets illustrate each point, and a final section shows how autonomous, persona‑driven exploration (such as that offered by SUSA) reinforces pagination testing without adding script‑maintenance overhead.
Why Pagination Testing Matters
Impact on UX and Performance
When a user scrolls through a list, each page request triggers a round‑trip to the backend. If the service returns inconsistent cursors, missing items, or duplicate rows, the user perceives the app as broken. Slow or failing pagination also inflates latency metrics, increases server load, and can cause cascading timeouts in micro‑service architectures. In 2026, with the rise of adaptive streaming feeds and infinite‑scroll patterns, a single pagination flaw can affect millions of daily active users.
Common Production Failures
Production incidents often stem from:
- Cursor drift – the server’s token does not advance correctly after a filter change.
- Page‑size overflow – a client requests 500 items when the backend caps at 100, leading to OOM or HTTP 413.
- Stale data – cached pagination state serves outdated results after a background write.
- Accessibility traps – focus jumps to invisible elements when a new page loads, breaking screen‑reader navigation.
- Security leaks – improper validation of offset/limit parameters enables enumeration attacks.
Understanding these patterns helps you prioritize tests that catch the most costly defects before they reach users.
Metrics that Matter
Track the following quantitative signals in your test suite and production monitoring:
| Metric | Why it matters | Target (2026) |
|---|---|---|
| Page‑load latency (p95) | Directly influences perceived responsiveness | < 200 ms |
| Cursor correctness rate | % of pages where next/prev tokens are accurate | 99.9 % |
| Duplicate‑item ratio | Measures data‑integrity regressions | < 0.01 % |
| Empty‑state handling | % of scenarios where zero‑result pages render correctly | 100 % |
| Accessibility violation count | WCAG 2.1 AA failures per pagination component | 0 |
| Rate‑limit throttling hits | Indicates client‑side over‑fetching | < 1 % of requests |
These metrics give you a concrete way to gauge test coverage and regression risk.
Core Principles of Pagination Testing
Contract‑Driven Testing
Treat the pagination API as a contract: given a set of input parameters (cursor, limit, sort, filters) the service must return a deterministic slice of data plus accurate navigation tokens. Write tests that assert the contract rather than asserting specific UI text. This approach survives UI redesigns and works equally well for GraphQL connections, REST offset/limit, or cursor‑based schemes.
State Isolation
Each pagination test should start from a clean server state. Use database snapshots, containerized test fixtures, or feature flags that reset seed data before every run. Shared state leads to false positives when a previous test leaves a half‑filled page that influences the next request.
Deterministic vs Non‑Deterministic Data
When possible, seed deterministic datasets (e.g., UUIDs with known lexical order) to make assertions about item positions easy. For features that rely on real‑time data (stock prices, social feeds), inject mock services that return controlled sequences while preserving the pagination contract.
Persona‑Driven Exploration
Different users interact with pagination in distinct ways:
- Curious – taps every item, explores deep pages.
- Impatient – jumps to the last page or uses fast‑scroll gestures.
- Novice – relies on visible page numbers, may miss “next” if it’s icon‑only.
- Adversarial – tries malformed cursors, negative limits, or huge page sizes.
- Elderly / Accessibility – needs larger touch targets, predictable focus order.
- Power user – uses keyboard shortcuts, expects shift‑+‑page‑up/down to work.
Modeling these personas in exploratory sessions surfaces edge cases that scripted tests miss.
Test Matrix: What to Verify
The following matrix organizes verification points by layer and risk level. Use it to decide which checks belong in unit tests, contract tests, UI automation, or manual exploratory sessions.
| Layer | Functional Checks | Edge Cases | Performance / Scalability | Accessibility | Security | |
|---|---|---|---|---|---|---|
| API / Service | Correct slice, token generation, sorting stability | Zero‑result, single‑item, max‑limit, invalid cursor, out‑of‑range offset | Latency under varying load, throughput with concurrent paginated requests | Proper HTTP status codes, CORS headers | Parameter injection, enumeration via limit/offset, token tampering | |
| UI / Web | Page renders correct items, next/prev buttons enabled/disabled correctly | Infinite scroll triggers, pull‑to‑refresh, page‑size changer, lazy‑load placeholders | Frame‑drop < 16 ms, network waterfall shows single request per scroll | Focus moves to first new item, ARIA live region announces new content, color contrast on controls | XSS via rendered item data, click‑jacking on pagination controls | |
| Mobile / Native | ListView/RecyclerView updates, swipe‑to‑load, pull‑to‑refresh | Orientation change mid‑scroll, background fetch while app is paused, low‑memory kill | Battery impact, CPU usage per page, jank < 2 frames | TalkBack announces new items, touch target ≥ 48 dp, scalable fonts | Clipboard leakage via long‑press on items, insecure token storage | |
| Cross‑cutting | End‑to‑end flow (login → paginated list → detail → back) | Network loss mid‑page, retry logic, stale‑cache handling | End‑to‑end latency, server‑side CPU spikes | Consistent‑Read isolation | Full WCAG audit on paginated screens | Session fixation, CSRF on state‑changing pagination actions |
Use this matrix as a living document: when a new pagination feature ships, tick the relevant cells and add any missing tests.
Manual Testing Techniques
Exploratory Sessions with Personas
Allocate 30‑minute time‑boxed sessions for each persona defined above. Provide a charter such as “As an impatient shopper, I want to reach the last page of results in under two taps.” Record the session with a tool like OBS or the built‑in recorder in Android Studio. After the session, debrief to capture observations:
- Did any gesture cause the list to jump incorrectly?
- Were there any dead zones where taps produced no response?
- Did screen‑reader announcements lag behind content updates?
Boundary Value Analysis
Apply classic BVA to pagination parameters:
- Limit: test 0, 1, (default‑1), default, (max‑1), max, max+1.
- Offset / Cursor: test first valid, last valid, first‑invalid, last‑invalid.
- Sort direction: ascending, descending, tie‑breaker fields.
Combine each limit value with each offset/boundary to create a combinatorial set; however, prune impossible combos (e.g., limit = 0 with any offset) to keep the set manageable.
Session Recording for Regression
Record a baseline interaction flow (e.g., login → browse 5 pages → logout) on a real device. Store the video and the associated network trace (HAR file). On each regression run, replay the recording using a tool like Selenium IDE or Playwright’s codegen, then compare the new HAR against the baseline. Differences in request timing, status codes, or payload size highlight regressions that unit tests might miss.
Checklist for Manual Testers
Give testers a lightweight, printable checklist that mirrors the matrix:
[ ] Verify first page loads correct items and shows disabled “previous”
[ ] Verify last page loads correct items and shows disabled “next”
[ ] Change page size via dropdown; ensure items per page update
[ ] Jump to page 10 via URL parameter; confirm correct slice
[ ] Attempt to load page with limit = 0; expect 400 or empty list
[ ] Attempt to load page with negative offset; expect 400
[ ] Turn on TalkBack; navigate through pages; ensure focus moves to first new item
[ ] Disable network; retry after reconnection; ensure no duplicate items
[ ] Resize browser window; ensure pagination controls remain accessible
[ ] Use keyboard: Shift+PageUp/PageDown shifts view correctly
Check off each item during exploratory testing; any failures become immediate bug tickets.
Automated Pagination Testing
Unit and Contract Tests
Start at the service layer. Write parameterized tests that call the pagination endpoint with a matrix of inputs and assert:
- Returned items count equals requested limit (or fewer if at end).
- The
nexttoken, when used in a subsequent request, yields items that continue where the previous left off. - The
prevtoken (if supported) returns the prior slice. - Sorting order is stable across pages.
Example (JUnit 5 + RestAssured):
@ParameterizedTest
@CsvSource({
"0,10", // offset, limit
"10,10",
"20,5",
"100,0" // edge case: limit 0
})
void testPaginationContract(int offset, int limit) {
Response first = given()
.queryParam("offset", offset)
.queryParam("limit", limit)
.when()
.get("/api/items")
.then()
.extract()
.response();
assertThat(first.statusCode()).isEqualTo(200);
List<Item> items = first.jsonPath().getList(".", Item.class);
assertThat(items).hasSize(limit == 0 ? 0 : limit);
if (limit > 0 && !items.isEmpty()) {
String nextToken = first.jsonPath().getString("nextToken");
Response second = given()
.queryParam("cursor", nextToken)
.when()
.get("/api/items")
.then()
.extract()
.response();
List<Item> secondItems = second.jsonPath().getList(".", Item.class);
// Ensure no overlap and correct ordering
assertThat(secondItems).noneMatch(item -> items.contains(item));
// Optional: verify that the first item of second page > last item of first page
}
}
Contract tests can be generated from OpenAPI/Swagger specifications using tools like Pact or Dredd, ensuring the contract stays versioned.
API‑Level Pagination Validation
Beyond unit tests, run a dedicated API test suite that treats the pagination endpoint as a black box. Use a data‑driven approach where a CSV or JSON file defines sequences of requests:
- name: "Walk through 5 pages of 20 items"
request:
method: GET
url: /api/products
query:
limit: 20
cursor: "" # empty for first page
extract:
- name: nextCursor
jsonPath: $.nextToken
assert:
- status: 200
- jsonPath: $.items.length
equals: 20
- name: "Fetch next page using cursor"
request:
method: GET
url: /api/products
query:
limit: 20
cursor: ${nextCursor}
assert:
- status: 200
- jsonPath: $.items.length
equals: 20
- jsonPath: $.items[0].id
greaterThan: ${lastIdFromPreviousPage}
Running this suite against a staging environment nightly catches drift in token generation or sorting logic.
UI Automation with Appium/Playwright
Automate the visual layer only for scenarios that are difficult to capture at the API level: infinite scroll, pull‑to‑refresh, and accessibility focus order. Keep UI tests lean by relying on the API contract tests for data correctness.
Playwright example (Web infinite scroll):
test('infinite scroll loads new items without duplication', async ({ page }) => {
await page.goto('/feed');
await page.waitForSelector('.post');
// Capture initial IDs
const firstIds = await page.$$eval('.post', els => els.map(e => e.dataset.id));
// Scroll to bottom
await page.evaluate(() => window.scrollTo(0, document.body.scrollHeight));
await page.waitForTimeout(1500); // allow network
const secondIds = await page.$$eval('.post', els => els.map(e => e.dataset.id));
// Expect new IDs, no duplicates
const allIds = [...firstIds, ...secondIds];
expect(new Set(allIds).size).toBe(allIds.length);
expect(secondIds.length).toBeGreaterThan(0);
});
Appium example (Android RecyclerView):
@Test
public void testPullToRefreshUpdatesList() {
AndroidElement list = driver.findElement(By.id("recycler_view"));
int initialCount = list.findElements(By.id("item_text")).size();
// Pull down
new TouchAction(driver)
.press(PointOption.point(0, 300))
.waitAction(WaitOptions.waitOptions(Duration.ofMillis(200)))
.moveTo(PointOption.point(0, 100))
.release()
.perform();
// Wait for refresh
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));
wait.until(drv -> {
int now = drv.findElement(By.id("recycler_view")).findElements(By.id("item_text")).size();
return now > initialCount;
});
int afterCount = driver.findElement(By.id("recycler_view"))
.findElements(By.id("item_text"))
.size();
assertTrue(afterCount > initialCount);
}
Keep UI tests under two minutes per run; otherwise they become a CI bottleneck.
Data‑Driven Test Generation
Leverage tools like Hypothesis (Python) or jqwik (Java) to generate random but valid pagination parameters, then assert invariants such as “no duplicate items across pages” or “the union of all pages equals the full dataset”. This approach surfaces edge cases that manual BVA might miss.
from hypothesis import given, strategies as st
@given(
limit=st.integers(min_value=0, max_value=100),
offset=st.integers(min_value=0, max_value=1000)
)
def test_no_duplicates(limit, offset):
resp = client.get("/items", params={"limit": limit, "offset": offset})
assert resp.status_code == 200
items = resp.json()["items"]
ids = [i["id"] for i in items]
assert len(ids) == len(set(ids)) # no duplicates
Using SUSA for Autonomous Exploration (Mention SUSA)
SUSA can augment the above automated suite by autonomously navigating the app with varied personas. After uploading an APK or pointing SUSA at a staging URL, configure it to enable the “pagination” behavior profile. SUSA will:
- Issue random scroll gestures, tap page numbers, and invoke pull‑to‑refresh.
- Vary page‑size controls and attempt malformed cursor strings.
- Record any crashes, ANRs, or accessibility violations tied to pagination.
- Export the discovered flows as Appium (Android) and Playwright (Web) scripts, which you can then commit to your repo as regression tests.
Because SUSA learns from each run, subsequent executions focus on previously unseen dead ends, gradually increasing coverage without manual test‑authoring effort. Use the generated scripts as a starting point, then refine assertions to match your contract expectations.
CI/CD Integration and Flaky Test Mitigation
Pipeline Stages
Integrate pagination tests across three pipeline stages:
- Fast feedback – unit and contract tests run on every pull request (≤ 2 minutes).
- Extended validation – API‑level data‑driven suites and UI smoke tests run on merge to main (≤ 10 minutes).
- Nightly deep dive – full SUSA autonomous exploration, load‑testing with paginated requests, and accessibility audits run on a scheduled schedule.
Parallel Execution and Sharding
Pagination API tests are inherently stateless; shard them by parameter ranges to achieve linear speed‑up. Example with GitHub Actions:
name: Pagination API Tests
on: [push, pull_request]
jobs:
test:
strategy:
matrix:
shard: [0,1,2,3]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- run: ./gradlew testPagination --tests "*Pagination*" --shard ${{
matrix.shard }} --total-shards 4
Artifact Collection and Reporting
After each run, archive:
- JUnit XML or TestResult JSON for trend analysis.
- HAR files capturing network traffic for latency inspection.
- Screenshots or video clips from UI tests for visual regression.
- SUSA exploration logs (JSON) that list discovered dead ends and newly generated scripts.
Feed these artifacts into a dashboard (Grafana, Datadog, or a custom internal tool) to track:
- Test pass/fail rate over time.
- Median pagination latency per page size.
- Number of accessibility violations discovered per‑script‑generation count (indicates how much new coverage SUSA is adding).
Handling Flaky Pagination Tests
Flakiness often stems from:
- Timing dependencies – waiting for a network response that varies with load.
- Non‑deterministic data – using a shared test database that receives background writes.
- UI animation races – asserting before a list finishes rendering.
Mitigation strategies:
- Deterministic test data – use fixtures or mock servers that return fixed sequences.
- Explicit waits – wait for a specific condition (e.g., presence of a new item with a known ID) rather than a fixed timeout.
- Retry wrapper – wrap flaky tests in a limited‑retry mechanism (e.g., Jest’s
retriesor Gradle’sflakyTestplugin) but treat a retry as a signal to investigate root cause. - Isolate UI layer – run UI tests against a mocked backend that serves deterministic pagination responses; reserve real‑backend tests for the API layer.
Tooling Overview
Open‑Source Frameworks
| Category | Tools | Strengths |
|---|---|---|
| API contract | Pact, Dredd, OpenAPI Generator | Language‑agnostic, CI‑friendly |
| UI Web | Playwright, Cypress, Selenium | Powerful selectors, auto‑wait, tracing |
| UI Mobile | Appium, Espresso (Android), XCUITest (iOS) | Real device or emulator support |
| Property‑based | Hypothesis (Python), jqwik (Java), FastCheck (JS) | Generates edge‑case inputs |
| Accessibility | axe‑core, Google Accessibility Testing Framework, QualiWeb | Integrated into unit/UI tests |
| Load / Stress | k6, Locust, Gatling | Simulate many concurrent paginated requests |
Commercial Platforms
- Sauce Labs / BrowserStack – provides real‑device clouds for Appium/Playwright parallel runs.
- Mabl – low‑code UI test creation with built‑in accessibility checks.
- Testim – AI‑based locator stability, useful for paginated lists that frequently change DOM structure.
- SusaTest – autonomous exploration platform (mentioned earlier) that generates regression scripts from its own runs.
SUSA Integration Notes
When you add SUSA to your toolchain:
- Upload the latest APK or provide a staging URL.
- Enable the “Pagination” behavior profile under “Personas”.
- Set a budget for exploration time (e.g., 15 minutes per build).
- Retrieve the generated Appium/Playwright scripts from the SUSA dashboard and add them to your repository under
tests/generated/susa. - Treat these scripts as baseline tests; augment them with specific assertions (e.g., check for duplicate IDs, verify accessibility tags) before committing.
Using SUSA reduces the manual effort required to maintain UI test suites while still benefiting from its persona‑driven, adaptive exploration.
Anti‑Patterns to Avoid
Hard‑Coded Page Sizes
Assuming a fixed limit=20 in UI tests hides bugs when the product team changes the default page size or when a power‑user selects “Show 100”. Always read the page‑size selector’s value or parameterize tests across the supported range.
Ignoring Empty States
A pagination component must gracefully handle zero results. Forgetting to test the empty‑state leads to missing UI elements, broken “next” button states, and screen‑reader confusion. Include explicit tests for:
- Initial load with no matching filters.
- Result set that becomes empty after applying a filter.
- Server returning an empty array with a
nextTokenofnull.
Over‑Reliance on Mock Data
Mocks that return static arrays can mask sorting or token‑generation bugs. Pair mock‑based unit tests with at least one end‑to‑end run against a real or near‑real dataset (e.g., a copy of production anonymized data). This hybrid approach catches contract violations that pure mocks miss.
Skipping Accessibility Checks
Pagination controls often rely on icons or custom widgets that lack proper ARIA labels. Run automated accessibility audits (axe‑core) on every pagination screen and manually verify with screen‑readers (TalkBack, VoiceOver). Log any violations as high‑severity bugs.
Forgetting Network Failure Scenarios
Users frequently lose connectivity mid‑scroll. Tests that only succeed on a perfect network give false confidence. Simulate latency, packet loss, and abrupt disconnects using tools like Toxiproxy or the network throttling features in Chrome DevTools. Verify that the app retries correctly, does not duplicate items, and shows appropriate offline indicators.
Prioritized Checklist
The following checklist distills the matrix and best practices into a ready‑to‑run list for release gates. Teams can copy‑paste this into their definition of done.
| ✅ Item | Description | Automation Suggestion |
|---|---|---|
| 1 | Contract test validates correct slice and token generation for all limit/offset combos | Unit/parameterized test |
| 2 | Zero‑result page renders proper empty state and disables navigation | UI test + accessibility check |
| 3 | Max supported limit returns expected number of items without error | API test |
| 4 | Invalid cursor or negative offset returns 400/422 | API test |
| 5 | Scrolling or pagination does not produce duplicate items across pages | Data‑driven test + set assertion |
| 6 | Focus moves to first newly loaded item after page change (keyboard & touch) | Playwright/Appium with accessibility inspector |
| 7 | Page‑size changer updates items per page and persists across navigation | UI test |
| 8 | Pull‑to‑refresh or manual reload does not lose current page token | UI test + network mock |
| 9 | Accessibility audit (axe) returns zero violations on pagination component | Automated axe run in CI |
| 10 | Simulated 3G latency and random packet loss still yields correct pagination | k6/Locust script with network throttling |
| 11 | SUSA exploration run generates no new crashes or ANRs related to pagination | SUSA run + log review |
| 12 | Generated regression scripts from Susa pass in CI | Add generated scripts to test suite |
| 13 | Performance: p95 page‑load latency < 200 ms under expected load | Load test with k6 |
| 14 | Security: limit/offset parameters resist injection and enumeration | OWASP ZAP active scan or custom fuzz |
| 15 | Documentation: API spec clearly defines pagination contract (cursor format, limit bounds) | Keep OpenAPI/Swagger up‑to‑date |
Mark each item as PASS, FAIL, or SKIP (with justification). Any FAIL blocks release until resolved.
Real‑World Examples and Lessons Learned
E‑commerce Infinite Scroll Bug
A major retailer introduced an infinite‑scroll product listing. Their automated suite only tested the first two pages. In production, users reported seeing the same product appear after scrolling past page 7. Root cause: the backend’s cursor token was a base64‑encoded offset that reset to zero after reaching a certain threshold due to integer overflow in a 32‑bit service. The fix involved switching to a UUID‑based cursor and adding a contract test that asserts monotonic increase of the token across pages. Lesson: Never trust opaque tokens without verifying their internal monotonicity.
Financial Dashboard Pagination Timeout
A fintech dashboard displayed transaction history using offset/limit pagination. Under load, the 95th‑percentile latency spiked to 2.3 seconds when users requested limit=500. The database lacked an index on the filtered column combined with the sort order, causing a full table scan. The team added a performance test that asserts latency stays under 800 ms for the max allowed limit and added the missing index. Lesson: Include pagination in performance baseline tests; limits are not just UI convenience.
Social Media Feed Accessibility Issue
A social app replaced traditional pagination with a “load more” button that used ARIA‑hidden to hide the button after activation. Screen‑reader users reported that new content was not announced, leaving them unaware that more posts existed. An axe audit flagged missing aria-live region. The fix added aria-live="polite" to the container and ensured focus moved to the first new item. Lesson: Automated accessibility checks must run on every UI change that alters live regions.
Future Trends and Takeaways
AI‑Driven Test Generation
Research prototypes now use large language models to produce pagination test scenarios directly from API specifications. Early adopters report a 30 % increase in edge‑case coverage with minimal manual effort. While still nascent, consider piloting such tools in a sandbox environment to augment your hypothesis‑based suites.
Observability‑First Testing
Teams are binding test results to production observability traces. By tagging each paginated request with a test‑run ID, they can correlate synthetic latency spikes with real‑user metrics, enabling faster regression detection. Implement this by injecting a custom header (X-Test-Run: ${UUID}) in your test harness and querying your tracing backend (Jaeger, Tempo) for anomalies.
Closing Takeaways
- Treat pagination as a formal contract; verify it at the API layer before investing in UI automation.
- Use a tiered test matrix (unit → contract → UI → exploratory) to allocate effort where it yields the highest defect detection.
- Persona‑driven exploration—whether manual or via platforms like SUSA—uncovers issues that scripted tests miss, especially around accessibility, network faults, and edge‑case user behaviors.
- Monitor latency, duplication, and token correctness as core metrics; set concrete SLOs and alert on drift.
- Avoid anti‑patterns such as hard‑coded limits, ignoring empty states, and skipping accessibility checks; they are the most common sources of production regressions.
- Integrate tests early and often in CI, sharding for speed, and retain artifacts for trend analysis.
- Leverage autonomous tools like SUSA to generate baseline regression scripts, then enrich them with domain‑specific assertions.
By following these practices, you’ll build confidence that your pagination component behaves correctly under the myriad conditions users encounter in the real world, keeping both your data integrity and your user experience intact. Happy testing!
Test Your App Autonomously
Upload your APK or URL. SUSA explores like 10 real users — finds bugs, accessibility violations, and security issues. No scripts.
Try SUSA Free