How to Test Pagination: A Complete Guide

How to Test Pagination: A Complete Guide: Pagination is a common UI pattern that lets users navigate large data sets by breaking them into discrete pages. When pagination is flawed, users can lose dat

February 28, 2026 · 17 min read · How-To Guides

How to Test Pagination: A Complete Guide: Pagination is a common UI pattern that lets users navigate large data sets by breaking them into discrete pages. When pagination is flawed, users can lose data, encounter endless loops, or be blocked from reaching content, which directly impacts conversion and satisfaction. This guide walks through why pagination matters, what typically breaks, a comprehensive test matrix, manual and automated techniques, accessibility and security considerations, production‑only edge cases, and how autonomous, persona‑driven exploration can surface bugs that scripted tests miss. Each section includes concrete examples, tables, and code snippets you can adapt to your stack.

How to Test Pagination: A Complete Guide: Why Pagination Matters

Pagination appears in lists, tables, infinite scrolls, and tabbed interfaces. It affects performance, usability, and data integrity. A broken pager can cause:

Testing pagination early prevents these defects from reaching production, where they are far more costly to fix. The following sections break down the testing effort into a repeatable matrix and practical techniques.

How to Test Pagination: A Complete Guide: Core Concepts and Types

Before designing tests, clarify the pagination implementation you are dealing with. Common variants include:

TypeDescriptionTypical ControlsCommon Pitfalls
Offset‑LimitServer receives offset and limit (or skip/take).“Previous”, “Next”, page numbers, jump‑to input.Off‑by‑one errors, negative offsets, limit > max allowed.
Cursor‑BasedServer returns an opaque token (cursor) for the next set.“Next” button only; sometimes “Prev”.Token expiration, token leakage, missing prev cursor.
Page‑NumberClient sends page index (1‑based).Numbered links, dropdown, “First”, “Last”.Page 0 or negative page, page > totalPages, stale total count.
Infinite ScrollNo explicit controls; more data loads on scroll or trigger.Scroll event, “Load more” button.Duplicate loads, missing sentinel, failure to stop at end.
Virtualized ListUI renders only visible rows; data fetched via scroll offset.Internal scroll position, resize listener.Incorrect item height calculation, stale cache, jump‑to failures.

Understanding which type you have informs the test cases you need. For example, cursor‑based systems require validation of token integrity, while offset‑limit needs boundary checks on offset and limit.

How to Test Pagination: A Complete Guide: Building a Test Matrix

A structured matrix ensures you cover happy paths, error paths, edge cases, accessibility, and security. Below is a comprehensive matrix you can copy into a test‑management tool. Each row represents a test scenario; columns indicate the pagination type(s) it applies to and the expected outcome.

IDScenarioApplies ToStepsExpected ResultNotes
P1Navigate to first page via UIAllClick “First” or page 1 linkShows first set of items, correct page indicatorVerify URL/query params
P2Navigate to last page via UIOffset‑Limit, Page‑NumberClick “Last” or highest page numberShows final set, “Next” disabledCheck that total items match last page size
P3Click “Next” repeatedly until lastAllRepeatedly press “Next”Each page shows new items, no duplicates, finally “Next” disabledCount total clicks vs. expected pages
P4Click “Previous” from first pageAllPress “Previous” on page 1“Previous” stays disabled, no navigationEnsure no error state
P5Jump to middle page via inputPage‑NumberEnter page number, submitShows correct subset, URL updatesValidate bounds (1 ≤ page ≤ totalPages)
P6Invalid page number (0, negative, non‑numeric)Page‑NumberEnter 0, -5, “abc”System shows error or defaults to page 1Should not crash or expose stack trace
P7Offset less than zeroOffset‑LimitManually set offset=-10Returns error or defaults to offset 0Backend validation
P8Limit larger than max allowedOffset‑LimitSet limit=10000 when max is 200Returns error or caps limitPrevents DoS
P9Non‑numeric offset/limitOffset‑LimitSet offset=tenError response (400)Input sanitization
P10Cursor token tamperingCursor‑BasedModify token charactersReturns error or empty setTokens should be opaque and signed
P11Missing “prev” cursor on first pageCursor‑BasedInspect network on page 1No prev token fieldUI should hide “Prev”
P12Duplicate items across pagesAllCompare item IDs on page n and n+1No overlapIndicates offset/limit miscalc
P13Missing items (gap)AllVerify sequential IDs with no gapsEvery expected ID appears somewhereGap suggests incorrect total count
P14Infinite scroll loads duplicate dataInfinite ScrollScroll to bottom, wait for load, scroll againNew items only, no repeatsCheck request parameters
P15Virtualized list item height mismatchVirtualized ListResize browser, scroll fastNo blank spikes, all items renderedUse debugger to inspect render offsets
P16Keyboard navigation – Tab through controlsAllTab to pager, use Arrow keysFocus moves logically, page changesVerify ARIA labels
P17Screen reader announces pageAllNavigate with NVDA/JAWSReads “Page X of Y”, “Next button disabled” when appropriateCheck live region updates
P18Color contrast of disabled controlsAllInspect CSS contrast ratio≥ 4.5:1 for normal textUse axe or similar
P19Touch target size ≥ 44 dpAllMeasure tap area on mobileMeets guidelineImportant for elderly/power‑user personas
P20Rate‑limit abuse via rapid page changesAllSend 100 requests/sec changing pageServer responds with 429 or similarPrevents scraping/DoS
P21SQL injection via page parameterOffset‑Limit, Page‑NumberInsert ' OR 1=1-- into page fieldNo data leakage, error responseUse parameterized queries
P22Path traversal via cursorCursor‑BasedInsert ../ in tokenToken rejected, no file accessValidate token format
P23CSP violation via injected script in page numberPage‑NumberSubmit Script not executed, sanitized outputOutput encoding
P24Load‑time spike on large page sizeOffset‑LimitSet limit=5000 (if allowed)Response time within SLA or errorPerformance guardrail
P25Stale total count after backend updateAllAdd/delete items, refresh pagerPage count updates correctlyMay require cache invalidation
P26Locale‑specific number formattingPage‑NumberSwitch language to Arabic, test pagerNumbers rendered correctly, RTL layouti18n considerations
P27Accessibility‑focused persona: elderly userAllSimulate tremor (large tap tolerance)Controls still operable, no mis‑tapsPersona‑driven test
P28Impatient persona: rapid next clicksAllClick “Next” five times within 200 msNo missed pages, no duplicate loadsTests debounce/throttle
P29Curious persona: explore jump‑to extremesPage‑NumberJump to page 1, then to last, then to middleConsistent state, no UI glitchesChecks state reset
P30Adversarial persona: malformed JSON in offsetOffset‑LimitSend { "offset": {"$gt":0} }Error response, no injectionTests input validation depth

You can adjust the matrix to your specific technology stack. Each scenario should have an automated test where feasible; manual exploratory testing is valuable for the persona‑driven rows (P27‑P30) and for spotting UI‑only glitches.

How to Test Pagination: A Complete Guide: Manual Testing Techniques

Manual testing remains essential for catching visual, interaction, and accessibility issues that automated scripts may overlook. Follow this procedure for each pagination component:

  1. Setup – Deploy a stable build with a known dataset (e.g., 250 items, page size = 25 → 10 pages). Seed the database with unique identifiers (UUIDs or sequential numbers) to simplify duplicate/gap detection.
  2. Baseline verification – Load the first page, confirm that the UI shows the correct subset, that the URL or internal state reflects page=1, and that “Previous” is disabled while “Next” is enabled.
  3. Linear navigation – Repeatedly click “Next” (or scroll for infinite scroll) until the last page appears. After each click, note:
  1. Reverse navigation – From the last page, click “Previous” back to the first, checking for symmetry.
  2. Jump‑to validation – Use the page‑number input or dropdown to jump to random pages (e.g., 3, 7, 9). Verify the displayed subset matches the expected offset.
  3. Boundary attempts – Try to navigate past the first or last page (e.g., click “Previous” on page 1, “Next” on last page). Ensure controls are disabled and no error state appears.
  4. Error injection – Manually edit network requests via browser dev tools (or a proxy like Burp) to send invalid offset/limit values, negative numbers, or non‑numeric strings. Observe that the API returns a 4xx error and the UI shows a user‑friendly message, not a stack trace.
  5. Accessibility check – Using only the keyboard, tab to the pager, use Arrow keys to change pages, and confirm that focus never gets lost. Run a screen reader (NVDA, VoiceOver, TalkBack) and listen for correct announcements.
  6. Performance observation – While navigating, watch the network tab. Ensure each request asks for only the intended slice (correct offset/limit or cursor). Note any requests that fetch the full dataset.
  7. Persona simulation

Document any deviations in a bug report with steps, expected vs. actual, screenshots, and network logs. Manual testing shines when assessing visual alignment, touch feedback, and screen‑reader nuances—areas where automated locators can be brittle.

How to Test Pagination: A Complete Guide: Automated Testing Strategies

Automated tests give you fast feedback on regression and help enforce the matrix at scale. Below are patterns for UI‑level and API‑level verification, with code snippets for Playwright (web) and Appium (Android). Adjust selectors and assertions to your framework.

API‑Level Pagination Tests

Validate that the backend respects offset/limit, cursor, and page parameters, and that it returns correct metadata (total count, next/prev tokens).


import requests
import pytest

BASE_URL = "https://api.example.com/items"
PAGE_SIZE = 10

def fetch_page(params):
    resp = requests.get(BASE_URL, params=params)
    resp.raise_for_status()
    return resp.json()

def test_offset_limit_happy_path():
    first = fetch_page({"offset":0, "limit":PAGE_SIZE})
    second = fetch_page({"offset":PAGE_SIZE, "limit":PAGE_SIZE})
    assert len(first["items"]) == PAGE_SIZE
    assert len(second["items"]) == PAGE_SIZE
    # ensure no overlap
    first_ids = {i["id"] for i in first["items"]}
    second_ids = {i["id"] for i in second["items"]}
    assert first_ids.isdisjoint(second_ids)

def test_invalid_offset():
    resp = requests.get(BASE_URL, params={"offset":-5, "limit":PAGE_SIZE})
    assert resp.status_code == 400
    assert "offset must be >= 0" in resp.text

def test_limit_exceeds_max():
    resp = requests.get(BASE_URL, params={"offset":0, "limit":5000})
    # Assuming server caps at 200
    assert resp.status_code == 400 or len(resp.json()["items"]) <= 200

UI‑Level Tests with Playwright (Web)

Playwright offers auto‑waiting and robust selectors. The following example tests a typical offset‑limit pager with numbered links.


// pagination.test.js
const { test, expect } = require('@playwright/test');

test.describe('Pagination component', () => {
  test.beforeEach(async ({ page }) => {
    await page.goto('https://app.example.com/products?page=1&size=10');
  });

  test('shows correct first page items', async ({ page }) => {
    const items = await page.locator('.product-item').count();
    expect(items).toBe(10);
    const firstId = await page.locator('.product-item').first().textContent();
    expect(firstId).toBe('PROD-0001');
  });

  test('navigates to next page without duplicates', async ({ page }) => {
    await page.click('button[aria-label="Next page"]');
    await page.waitForURL(/page=2/);
    const secondPageIds = await page.locator('.product-item')
      .evaluateAll(el => el.map(e => e.textContent().trim()));
    const firstPageIds = await page.locator('.product-item')
      .evaluateAll(el => el.map(e => e.textContent().trim()));
    expect([...new Set([...firstPageIds, ...secondPageIds])].length).toBe(20);
  });

  test('disables previous button on first page', async ({ page }) => {
    const prevBtn = page.locator('button[aria-label="Previous page"]');
    await expect(prevBtn).toBeDisabled();
  });

  test('jump‑to page works and updates URL', async ({ page }) => {
    await page.fill('input[name="page"]', '7');
    await page.press('input[name="page"]', 'Enter');
    await page.waitForURL(/page=7/);
    const items = await page.locator('.product-item').count();
    expect(items).toBe(10);
    const firstId = await page.locator('.product-item').first().textContent();
    expect(firstId).toBe('PROD-00061');
  });

  test('invalid page input shows error', async ({ page }) => {
    await page.fill('input[name="page"]', '0');
    await page.press('input[name="page"]', 'Enter');
    await expect(page.locator('.error-message')).toHaveText(/Page must be ≥ 1/);
  });

  test('keyboard navigation changes page', async ({ page }) => {
    await page.focus('button[aria-label="Next page"]');
    await page.press('ArrowRight'); // assumes right arrow triggers next
    await page.waitForURL(/page=2/);
    const btn = await page.locator('button[aria-label="Next page"]');
    await expect(btn).toBeEnabled();
  });
});

Mobile Tests with Appium (Android)

For native Android lists that use RecyclerView with pagination via scroll, you can verify that new items load without duplication.


// PaginationTest.java
import io.appium.java_client.android.AndroidDriver;
import org.junit.jupiter.api.*;
import org.openqa.selenium.By;
import java.util.HashSet;
import java.util.Set;

@TestInstance(TestInstance.Lifecycle.PER_CLASS)
public class PaginationTest {
    private AndroidDriver driver;

    @BeforeAll
    public void setUp() {
        // Initialize driver with desired caps
        driver = new AndroidDriver(new URL("http://localhost:4723/wd/hub"), caps);
    }

    @AfterAll
    public void tearDown() {
        if (driver != null) driver.quit();
    }

    @Test
    public void scrollLoadsNewItemsWithoutDuplicates() {
        Set<String> seenIds = new HashSet<>();
        int scrolls = 0;
        final int maxScrolls = 20; // safety guard

        while (scrolls < maxScrolls) {
            // collect visible item IDs
            List<MobileElement> items = driver.findElements(By.id("item_id"));
            for (MobileElement el : items) {
                String id = el.getText();
                Assert.assertFalse("Duplicate ID found: " + id, seenIds.contains(id));
                seenIds.add(id);
            }
            // scroll to bottom
            driver.swipe(500, 1500, 500, 500, 800);
            scrolls++;
        }
        Assert.assertTrue("Expected at least 100 unique items", seenIds.size() >= 100);
    }
}

These snippets illustrate the core ideas: validate data integrity, check control states, and ensure error handling. Integrate them into your CI pipeline to run on every commit.

How to Test Pagination: A Complete Guide: Accessibility and Security Checks

Pagination intersects with accessibility (WCAG 2.1 AA) and security (OWASP). Treat them as first‑class test categories, not afterthoughts.

Accessibility Checklist

WCAG CriterionHow to Verify for Pagination
1.3.1 Info and RelationshipsEnsure page numbers are announced as a list or group (role="list" or aria-label="Pagination").
2.1.1 KeyboardAll pager controls reachable via Tab; Arrow keys change page without needing mouse.
2.4.7 Focus VisibleWhen a pager button receives focus, a visible outline (≥ 2 px CSS) appears.
2.4.6 Headings and LabelsEach page link has a discernible text label (not just an icon).
2.5.3 Label in NameVoice control users can say “click next” and the button responds.
4.1.2 Name, Role, ValueDisabled buttons have aria-disabled="true" and are announced as disabled.
1.4.3 Contrast (Minimum)Text and icons meet 4.5:1 contrast against background.
1.4.10 ReflowOn zoom to 200 %, pager controls do not lose content or functionality.
1.4.11 Non‑text ContrastActive/inactive states have sufficient contrast (≥ 3:1).
2.5.5 Target SizeTouch targets ≥ 44 × 44 dp (or 48 × 48 dp for WCAG 2.2).

Automated tools like axe-core, Pa11y, or Lighthouse can catch many of these, but manual verification with screen readers and keyboard‑only navigation is essential for complex custom widgets.

Security Checklist

OWASP / CWEPagination‑Specific Test
A01:2021 – Broken Access ControlEnsure users cannot bypass pagination to view unauthorized records (e.g., by manipulating offset to jump into another tenant’s data).
A03:2021 – InjectionValidate that page, offset, limit, or cursor parameters are properly typed and parameterized; attempt SQL, NoSQL, and command injection.
A05:2021 – Security MisconfigurationConfirm that error messages do not reveal stack traces or internal DB structure when invalid pagination values are supplied.
A06:2021 – Vulnerable and Outdated ComponentsCheck that any third‑party pagination library is up‑to‑date and has no known CVEs.
A07:2021 – Identification and Authentication FailuresIf pagination exposes user‑specific data, verify that authentication is enforced on each page request.
A08:2021 – Software and Data Integrity FailuresEnsure that cursor tokens are signed or encrypted; tampering should be rejected.
A09:2021 – Security Logging and Monitoring FailuresLog malformed pagination attempts (e.g., negative offset) and alert on spikes.
A10:2021 – Server‑Side Request Forgery (SSRF)If pagination triggers internal API calls based on user input, verify that the input cannot be used to reach internal services.

Automated security scanners (OWASP ZAP, Burp Suite) can be pointed at pagination endpoints with fuzzing payloads. Incorporate these scans into your nightly security pipeline.

How to Test Pagination: A Complete Guide: Production‑Only Edge Cases

Some defects only manifest under real‑world load, data variance, or deployment specifics. Anticipate them with targeted prod‑like testing.

Data Skew and Distribution

If your data set is not uniformly distributed (e.g., power‑law distribution of item sizes), a fixed limit may cause highly variable response times. Test with:

Concurrency and Race Conditions

Under high traffic, simultaneous updates to the underlying dataset can cause:

Mitigate by using snapshot isolation or version‑based cursors (e.g., include a timestamp or transaction ID in the token). In test, simulate concurrent writes with tools like Locust or k6 while iterating through pages.

Caching and CDI Effects

If your API sits behind a cache (e.g., Varnish, CloudFront), pagination parameters must be part of the cache key. Otherwise:

Test by purging cache between requests and verifying that the response reflects the latest state. Use cache‑busting query strings or headers to ensure each request is unique.

Locale and Proxy

Some CDN:

query strings (?_ts=timestamp) or vary‑by on pagination parameters.

Locale and Timezone

Pagination that relies on date‑based cursors (e.g., “load newer items since X”) can be tripped by:

Validate by sending requests with various Accept-Language headers and checking that the returned data set matches expectations for each locale.

Device‑Specific Rendering

On low‑end devices, large pagination controls may cause layout thrash. Test with:

Monitoring and Alerting

Production pagination faults often surface as increased latency or error rates. Instrument:

Set thresholds based on historical baselines; a sudden increase often points to a pagination regression.

How to Test Pagination: A Complete Guide: Using Autonomous, Persona‑Driven Exploration (Mention SUSA)

Scripted tests excel at checking known paths, but they can miss emergent behaviors that arise when real users interact with the app in unpredictable ways. Autonomous exploration platforms—like SUSATest—address this gap by:

  1. Generating realistic user sessions without pre‑written scripts. The agent explores the UI, taps, scrolls, types, and handles dialogs based on a behavior model.
  2. Applying multiple personas (curious, impatient, novice, adversarial, elderly, accessibility, power user, etc.) each with distinct interaction patterns (e.g., impatient users double‑tap quickly; elderly users linger on controls; adversarial users try malformed inputs).
  3. Tracking navigation flows and marking each as PASS/FAIL based on observed crashes, ANRs, dead buttons, WCAG violations, security red flags, or UX friction.
  4. Creating regression scripts from the discovered flows: Appium scripts for Android and Playwright scripts for web, so you can retain the valuable scenarios found during exploration.
  5. Learning across sessions: the agent remembers which screens are dead ends and which paths are productive, making each run smarter and reducing redundant effort.

When applied to pagination, an autonomous agent can:

To run SUSATest on your app:


# Install the CLI
pip install susatest-agent

# Point at an Android APK or a web URL
susatest run --apk ./my-app.apk --personas curious impatient elderly
# or
susatest run --url https://shop.example.com/products --personas accessibility adversarial

The output includes a detailed report, a list of discovered flows with PASS/FAIL status, and generated test scripts you can commit to your repository. While autonomous testing does not replace targeted unit or API tests, it complements them by surfacing edge cases that are difficult to anticipate manually.

How to Test Pagination: A Complete Guide: Checklist and Takeaways

Use this concise checklist before signing off a pagination feature. Each item can be mapped to a test case in your test‑management tool.

✅ Functional Checklist

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