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

February 16, 2026 · 16 min read · Testing Guides

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:

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:

MetricWhy it mattersTarget (2026)
Page‑load latency (p95)Directly influences perceived responsiveness< 200 ms
Cursor correctness rate% of pages where next/prev tokens are accurate99.9 %
Duplicate‑item ratioMeasures data‑integrity regressions< 0.01 %
Empty‑state handling% of scenarios where zero‑result pages render correctly100 %
Accessibility violation countWCAG 2.1 AA failures per pagination component0
Rate‑limit throttling hitsIndicates 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:

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.

LayerFunctional ChecksEdge CasesPerformance / ScalabilityAccessibilitySecurity
API / ServiceCorrect slice, token generation, sorting stabilityZero‑result, single‑item, max‑limit, invalid cursor, out‑of‑range offsetLatency under varying load, throughput with concurrent paginated requestsProper HTTP status codes, CORS headersParameter injection, enumeration via limit/offset, token tampering
UI / WebPage renders correct items, next/prev buttons enabled/disabled correctlyInfinite scroll triggers, pull‑to‑refresh, page‑size changer, lazy‑load placeholdersFrame‑drop < 16 ms, network waterfall shows single request per scrollFocus moves to first new item, ARIA live region announces new content, color contrast on controlsXSS via rendered item data, click‑jacking on pagination controls
Mobile / NativeListView/RecyclerView updates, swipe‑to‑load, pull‑to‑refreshOrientation change mid‑scroll, background fetch while app is paused, low‑memory killBattery impact, CPU usage per page, jank < 2 framesTalkBack announces new items, touch target ≥ 48 dp, scalable fontsClipboard leakage via long‑press on items, insecure token storage
Cross‑cuttingEnd‑to‑end flow (login → paginated list → detail → back)Network loss mid‑page, retry logic, stale‑cache handlingEnd‑to‑end latency, server‑side CPU spikesConsistent‑Read isolationFull WCAG audit on paginated screensSession 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:

Boundary Value Analysis

Apply classic BVA to pagination parameters:

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:

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:

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:

  1. Fast feedback – unit and contract tests run on every pull request (≤ 2 minutes).
  2. Extended validation – API‑level data‑driven suites and UI smoke tests run on merge to main (≤ 10 minutes).
  3. 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:

Feed these artifacts into a dashboard (Grafana, Datadog, or a custom internal tool) to track:

Handling Flaky Pagination Tests

Flakiness often stems from:

Mitigation strategies:

  1. Deterministic test data – use fixtures or mock servers that return fixed sequences.
  2. Explicit waits – wait for a specific condition (e.g., presence of a new item with a known ID) rather than a fixed timeout.
  3. Retry wrapper – wrap flaky tests in a limited‑retry mechanism (e.g., Jest’s retries or Gradle’s flakyTest plugin) but treat a retry as a signal to investigate root cause.
  4. 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

CategoryToolsStrengths
API contractPact, Dredd, OpenAPI GeneratorLanguage‑agnostic, CI‑friendly
UI WebPlaywright, Cypress, SeleniumPowerful selectors, auto‑wait, tracing
UI MobileAppium, Espresso (Android), XCUITest (iOS)Real device or emulator support
Property‑basedHypothesis (Python), jqwik (Java), FastCheck (JS)Generates edge‑case inputs
Accessibilityaxe‑core, Google Accessibility Testing Framework, QualiWebIntegrated into unit/UI tests
Load / Stressk6, Locust, GatlingSimulate many concurrent paginated requests

Commercial Platforms

SUSA Integration Notes

When you add SUSA to your toolchain:

  1. Upload the latest APK or provide a staging URL.
  2. Enable the “Pagination” behavior profile under “Personas”.
  3. Set a budget for exploration time (e.g., 15 minutes per build).
  4. Retrieve the generated Appium/Playwright scripts from the SUSA dashboard and add them to your repository under tests/generated/susa.
  5. 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:

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.

✅ ItemDescriptionAutomation Suggestion
1Contract test validates correct slice and token generation for all limit/offset combosUnit/parameterized test
2Zero‑result page renders proper empty state and disables navigationUI test + accessibility check
3Max supported limit returns expected number of items without errorAPI test
4Invalid cursor or negative offset returns 400/422API test
5Scrolling or pagination does not produce duplicate items across pagesData‑driven test + set assertion
6Focus moves to first newly loaded item after page change (keyboard & touch)Playwright/Appium with accessibility inspector
7Page‑size changer updates items per page and persists across navigationUI test
8Pull‑to‑refresh or manual reload does not lose current page tokenUI test + network mock
9Accessibility audit (axe) returns zero violations on pagination componentAutomated axe run in CI
10Simulated 3G latency and random packet loss still yields correct paginationk6/Locust script with network throttling
11SUSA exploration run generates no new crashes or ANRs related to paginationSUSA run + log review
12Generated regression scripts from Susa pass in CIAdd generated scripts to test suite
13Performance: p95 page‑load latency < 200 ms under expected loadLoad test with k6
14Security: limit/offset parameters resist injection and enumerationOWASP ZAP active scan or custom fuzz
15Documentation: 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

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