How to Write Test Cases for Pagination (With Examples)

How to Write Test Cases for Pagination (With Examples) is a practical guide that walks you through the anatomy of a pagination test case, shows you how to derive positive, negative, boundary, and edge

January 05, 2026 · 15 min read · How-To Guides

How to Write Test Cases for Pagination (With Examples) is a practical guide that walks you through the anatomy of a pagination test case, shows you how to derive positive, negative, boundary, and edge‑case scenarios, and ties those cases to requirements and autonomous exploration. The goal is to give you a reusable test matrix you can execute manually or automate, plus a short checklist you can bookmark for future pagination features.

Understanding Pagination Mechanics

Pagination splits a large result set into smaller, consumable chunks. Whether the data comes from a relational database, a NoSQL store, or a REST API, the core concepts are the same:

When a client requests page N, the service typically computes skip = (N‑1) * pageSize and returns items skip … skip+pageSize‑1. Edge conditions arise when:

Understanding these mechanics lets you map each variable to a test condition and predict the exact system behavior.

Test Case Anatomy for Pagination

A well‑structured test case contains five essential parts:

ElementDescriptionExample for pagination
IDUnique identifier (e.g., PG-01)PG-01
PreconditionsState that must exist before execution200 items in DB, page size = 10, user logged in
StepsOrdered actions the tester or automation performs1. GET /items?page=1&size=10 2. Verify response contains items 1‑10
Expected ResultObservable outcome that determines PASS/FAILResponse status 200, body includes total=200, page=1, size=10, items array length = 10, next link present
Post‑conditions (optional)State left after the test (useful for chaining)DB unchanged, no side‑effects

Keep each step atomic and avoid bundling multiple assertions into a single step; this makes failure diagnosis faster. When writing automated scripts, map each step to a function or keyword (e.g., Playwright’s await page.goto(url)).

How to Write Test Cases for Pagination (With Examples): Positive and Boundary Cases

Positive cases verify that the system behaves correctly when inputs are within the declared contract. Boundary cases push those inputs to the limits of the contract (minimum, maximum, just‑inside, just‑outside). Below is a table of 22 representative cases. Feel free to copy the IDs into your test management tool and adjust the preconditions to match your domain.

Test Matrix (Positive & Boundary)

IDPreconditionsStepsExpected Result
PG-010 items in collection, pageSize = 10GET /items?page=1&size=10200 OK, total=0, items=[], no next link
PG-025 items, pageSize = 10GET /items?page=1&size=10200 OK, total=5, items length = 5, no next
PG-0310 items, pageSize = 10GET /items?page=1&size=10200 OK, total=10, items length = 10, no next
PG-0411 items, pageSize = 10GET /items?page=1&size=10200 OK, total=11, items length = 10, next link present
PG-05100 items, pageSize = 10GET /items?page=10&size=10200 OK, total=100, items items 91‑100, next absent, prev present
PG-06100 items, pageSize = 10GET /items?page=11&size=10200 OK, total=100, items=[], next absent, prev present
PG-07100 items, pageSize = 1GET /items?page=50&size=1200 OK, total=100, single item #50, next & prev present
PG-08100 items, pageSize = 100GET /items?page=1&size=100200 OK, total=100, items length = 100, no navigation links
PG-09100 items, pageSize = 101GET /items?page=1&size=101200 OK (or 400 if server rejects oversized size), total=100, items length = 100, no next
PG-10100 items, pageSize = 0GET /items?page=1&size=0200 OK, total=100, items=[], pagination links may be hidden or disabled
PG-11100 items, pageSize = -5GET /items?page=1&size=-5400 Bad Request (validation error)
PG-12100 items, page = 0GET /items?page=0&size=10400 Bad Request (page numbers start at 1)
PG-13100 items, page = -1GET /items?page=-1&size=10400 Bad Request
PG-14100 items, page = 1000000GET /items?page=1000000&size=10200 OK, total=100, items=[], navigation disabled
PG-15100 items, cursor token based (e.g., base64 of offset)Request first page, extract next token, request using token200 OK, returns items 11‑20, token updated correctly
PG-16100 items, cursor token expiredUse token from a previous run after DB reset401/400 (token invalid) or fallback to first page
PG-17100 items, token tampered (flip a bit)Send altered token400 Bad Request
PG-18100 items, mixed sort order (ASC/DESC)GET /items?page=2&size=10&sort=name,asc then descItems appear in correct order, no duplication/gaps
PG-19100 items, filter applied (status=active)GET /items?page=1&size=10&status=activeOnly active items returned, pagination respects filtered total
PG-20100 items, multiple filters (status=active&type=premium)GET with both query paramsPagination works on intersected result set
PG-21100 items, request includes unwanted params (page=1&size=10&debug=true)Send request with extra paramServer ignores unknown param, returns correct page
PG-22100 items, large pageSize (e.g., 1000)GET /items?page=1&size=1000200 OK, returns all 100 items, total=100, no next

Each case isolates a single variable (page number, page size, cursor, sort, filter) while holding others constant. This makes it easy to trace a failure back to a specific requirement, such as “page size must be a positive integer ≤ maxPageSize”.

How to Write Test Cases for Pagination (With Examples): Negative and Error Cases

Negative cases probe how the system reacts when inputs violate the contract or when unexpected conditions arise. They are crucial for guarding against crashes, information leakage, or poor UX.

IDPreconditionsStepsExpected Result
PGN-01100 items, pageSize = 10GET /items?page=abc&size=10400 Bad Request, validation message for non‑numeric page
PGN-02100 items, pageSize = 10GET /items?page=1&size=xyz400 Bad Request, validation message for non‑numeric size
PGN-03100 items, pageSize = 10GET /items?page=1&size=10&page=2 (duplicate param)Either 400 or uses the last value; document which behavior is expected
PGN-04100 items, pageSize = 10GET /items?page=1&size=10 with invalid Authorization header401 Unauthorized
PGN-05100 items, pageSize = 10GET /items?page=1&size=10 after DB connection loss503 Service Unavailable or retry‑after header
PGN-06100 items, pageSize = 10GET /items?page=1&size=10 with payload size > limit (e.g., oversized JWT)413 Payload Too Large or 400
PGN-07100 items, pageSize = 10GET /items?page=1&size=10 while requesting a non‑existent sort field400 Bad Request, message indicating unknown sort
PGN-08100 items, pageSize = 10GET /items?page=1&size=10 with a range header that conflicts with pagination416 Range Not Satisfiable (if server implements RFC 7233)
PGN-09100 items, pageSize = 10Simulate network latency (e.g., tc netem) and abort request mid‑streamClient receives a network error; server logs no partial response
PGN-10100 items, pageSize = 10Send request with page=1&size=10&_method=PUT (method tampering)405 Method Not Allowed
PGN-11100 items, pageSize = 10Request with page=1&size=10 while another process deletes the last item between page 9 and page 10Page 10 may be empty or contain shifted items; verify that the API does not throw 500
PGN-12100 items, pageSize = 10Request with page=1&size=10 and an illegal character in the token (e.g., null byte)400 Bad Request
PGN-13100 items, pageSize = 10Request with page=1&size=10 and Accept: application/xml when only JSON is supported406 Not Acceptable
PGN-14100 items, pageSize = 10Request with page=1&size=10 and If-None-Match matching an ETag for a different page304 Not Modified (if caching implemented) or 200 with correct page
PGN-15100 items, pageSize = 10Send a huge number of pagination requests in a short burst (rate‑limit test)After threshold, receive 429 Too Many Requests with retry‑after
PGN-16100 items, pageSize = 10GET /items?page=1&size=10 while the server is configured with a maxPageSize of 50 and request asks for size=100400 Bad Request or server caps size to max and returns adjusted page (document which)
PGN-17100 items, pageSize = 10Request with page=1&size=10 and a future timestamp in If-Modified-Since header200 OK (or 304 if resource not modified) – verify server ignores future dates
PGN-18100 items, pageSize = 10Request with page=1&size=10 and Connection: close headerResponse includes Connection: close and socket closes after body
PGN-19100 items, pageSize = 10Request with page=1&size=10 and Upgrade: websocket400 Bad Request (or 426 Upgrade Required)
PGN-20100 items, pageSize = 10Request with page=1&size=10 and a malicious SQL injection attempt in sort parameter400 Bad Request; no SQL error leaked in response body

These cases ensure that validation, error handling, security, and resilience mechanisms are in place. When automating, assert on HTTP status codes, response headers, and that error messages do not contain stack traces or internal identifiers.

How to Write Test Cases for Pagination (With Examples): Edge Cases and Production‑Only Scenarios

Some defects only manifest under load, with real‑world data distributions, or when external systems behave unexpectedly. Capture them in your test plan even if they are hard to reproduce in a clean test environment.

IDPreconditionsStepsExpected Result
PGE-0110 million items, pageSize = 100GET /items?page=50000&size=100 (deep page)Response within SLA (e.g., < 2 s), correct items 4 999 901‑5 000 000
PGE-0210 million items, pageSize = 100Repeatedly request pages 1‑100 in rapid successionNo memory leak; steady CPU usage; each page returns correct slice
PGE-03Items have variable length strings (average 2 KB, some 1 MB)GET /items?page=1&size=10Payload size reflects actual data; no truncation or corruption
PGE-04Items contain Unicode emojis and RTL charactersGET /items?page=1&size=10Characters render correctly in UI; no encoding errors
PGE-05Items have null values in sortable columnGET /items?page=1&size=10&sort=nullableField,ascNulls appear consistently (either first or last per DB collation)
PGE-06Items have duplicate sort keysGET /items?page=1&size=10&sort=nonUniqueField,ascStable ordering: same duplicates appear in same relative order across pages
PGE-07Real‑time feed: new items inserted while pagingRequest page 1, wait 5 s, request page 2Page 2 reflects the state at the time of its request; no duplication or missing items caused by intervening inserts
PGE-08Items soft‑deleted (flag isDeleted=true)GET /items?page=1&size=10&includeDeleted=falseOnly non‑deleted items returned; pagination count excludes soft‑deleted rows
PGE-09Items hard‑deleted between page 1 and page 2 requestsRequest page 1, delete last item of page 1, request page 2Page 2 shows the next item after the deleted one; total count decreased by 1
PGE-10Mixed read/write workload (other service updates same table)Run a background job that updates 100 random rows per second while paginatingNo dirty reads; each page shows a consistent snapshot (if using repeatable read or MVCC)
PGE-11API behind a CDN that caches based on query stringRequest page 1, then page 2, then page 1 againSecond request for page 1 returns cached copy (if allowed) with correct data; ensure stale‑while‑revalidate behavior matches contract
PGE-12Mobile client on high latency (300 ms RTT) and lossy link (5 % packet loss)Use tc to emulate network, perform infinite‑scrollUI shows loading spinner, retries on failure, eventually displays correct items
PGE-13Screen‑reader user navigating pagination controlsUse VoiceOver/TalkBack to move focus to “Next” button after last pageButton is announced as disabled; no unexpected focus shift
PGE-14User with motor impairments uses switch device to activate “Next”Simulate switch activationActivation works, no double‑trigger due to bounce
PGE-15Power user appends custom pageSize=9999 via devtoolsAttempt to set size via UI overrideServer rejects or caps to maxPageSize; UI reflects the capped value
PGE-16Adversarial user sends page=-9223372036854775808 (min int64)GET with that value400 Bad Request; no overflow or server crash
PGE-17Request includes a huge number of pagination parameters (page=1&size=10&page=2&size=20…)Send with many duplicatesServer either uses last occurrence or returns 400; behavior must be documented and consistent
PGE-18API versioning: client calls v1 endpoint that uses offset pagination while server migrated to cursor paginationCall v1 endpointEither returns 410 Gone or provides shim that translates offset to cursor; ensure no 500
PGE-19Server returns total as a floating‑point number due to ORM bugGET any pageClient detects type mismatch; either server fixes or client safely casts to integer
PGE-20Client mistakenly sends page=1.5 (non‑integer float)GET with float400 Bad Request; no silent coercion to 1 or 2

These edge cases often surface only after the system has been in production for weeks or months, especially when data volume grows or when third‑party networks fluctuate. Documenting them helps you prioritize load‑testing, chaos‑engineering, and accessibility validation.

Prioritization, Traceability, and Data Setup

Not all test cases carry the same risk. Use a simple risk‑based matrix that combines impact (how severe a failure would be) and likelihood (how often the condition occurs in production).

PriorityImpactLikelihoodExample IDs
P0Crash, data corruption, security breachHighPGN-01, PGN-04, PGN-11, PGE-08, PGE-09
P1Functional defect (wrong page, missing items)MediumPG-02, PG-04, PG-07, PG-15, PGE-01, PGE-03
P2UI glitch, performance degradation, minor error messageLowPGN-09, PGN-12, PGN-18, PGE-11, PGE-14
P3Cosmetic, documentation mismatchVery lowPGN-15, PGN-19, PGE-16

Traceability – Map each test case to a requirement ID from your specification (e.g., `REQ-PAG-001: “The API shall return exactly pageSize items unless fewer remain.”). Keep a traceability matrix in your test management tool or a simple spreadsheet:

RequirementVerified By
REQ-PAG-001PG-01, PG-02, PG-03, PG-04, PG-05, PG-06, PG-07, PG-08, PG-09, PG-10
REQ-PAG-002PGN-01, PGN-02, PGN-03, PGN-11
REQ-PAG-003PG-15, PG-16, PG-17
REQ-PAG-004PGE-01, PGE-02, PGE-03
REQ-PAG-005PGN-04, PGN-05, PGN-06
REQ-PAG-006PGE-08, PGE-09
REQ-PAG-007PGN-13, PGN-14
REQ-PAG-008PGE-11, PGE-12
REQ-PAG-009PGN-16, PGN-17
REQ-PAG-010PGE-13, PGE-14, PGE-15

Data Setup – Automate the creation of the baseline dataset using scripts or Docker containers. For a relational database you might use:


-- create_base_data.sql
TRUNCATE TABLE items;
INSERT INTO items (id, name, status, type, created_at)
SELECT
  generate_series(1,1000000) AS id,
  'Item-' || generate_series(1,1000000) AS name,
  CASE WHEN (random() * 100) < 20 THEN 'inactive' ELSE 'active' END AS status,
  CASE WHEN (random() * 100) < 10 THEN 'premium' ELSE 'standard' END AS type,
  now() - (random() * interval '365 days') AS created_at
FROM generate_series(1,1000000);

For NoSQL or document stores, use a bulk import tool (e.g., mongoimport with a JSON array). After each test run that mutates state (delete, update), either roll back a transaction or reseed the database from a known snapshot. Keep the seed script under version control so every environment (dev, CI, staging) starts identical.

Manual vs Automated Execution Strategies

Manual Execution

Automated Execution


import requests

def get_page(base_url, page, size):
    resp = requests.get(
        f"{base_url}/items",
        params={"page": page, "size": size},
        headers={"Accept": "application/json"},
        timeout=5,
    )
    resp.raise_for_status()
    return resp.json()

# Positive case PG-04
data = get_page("https://api.example.com", page=1, size=10)
assert data["total"] == 11
assert len(data["items"]) == 10
assert data["links"]["next"] is not None
assert data["links"]["prev"] is None

from playwright.sync_api import expect, sync_playwright

def test_next_button_disabled_on_last_page():
    with sync_playwright() as p:
        page = p.chromium.launch(headless=False).new_page()
        page.goto("https://app.example.com/items")
        # set page size to 10 via UI
        page.select_option("select#page-size", "10")
        # navigate to last page (calculate from total count)
        total = int(page.inner_text("span#total-count"))
        page_size = 10
        last_page = (total + page_size - 1) // page_size
        page.goto(f"?page={last_page}&size={page_size}")
        expect(page.locator("button#next")).to_be_disabled()

@Test
public void testInfiniteScrollLoadsMoreItems() {
    AndroidDriver<MobileElement> driver = getDriver();
    // Wait for initial list
    WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));
    wait.until(ExpectedConditions.numberOfElementsToBeMoreThan(
            By.id("item_row"), 0));
    int initialCount = driver.findElements(By.id("item_row")).size();

    // Scroll to bottom
    MobileElement list = driver.findElement(By.id("recycler_view"));
    ((JavascriptExecutor) driver).executeScript(
            "mobile: swipe", ImmutableMap.of(
                    "element", list.getId(),
                    "direction", "down",
                    "percent", 0.9));

    // Wait for new items
    wait.until(ExpectedConditions.numberOfElementsToBeMoreThan(
            By.id("item_row"), initialCount));
    assertTrue(driver.findElements(By.id("item_row")).size() > initialCount);
}

Leveraging Autonomous Exploration (SUSA) for Pagination Coverage

SUSA explores an application without pre‑written scripts, generating real user interactions and capturing any anomalies. When you point SUSA at a paginated list, it will:

  1. Discover navigation controls – It taps “Next”, “Previous”, page numbers, and infinite‑scroll triggers, learning which actions produce new content.
  2. Vary personas – The curious persona may keep tapping “Next” until it hits the end; the impatient persona may jump to a high page number via URL manipulation; the accessibility persona will use screen‑reader gestures to verify announcements.
  3. Detect regressions – If a recent change disables the “Next” button on the last page, SUSA will record a failure because the curious‑end state is missing.
  4. Generate regression scripts – After a run, SUSA outputs Appium (Android) and Playwright (Web) scripts that reproduce the exact taps, scrolls, and inputs it performed. You can add these to your CI suite as a safety net.

How to invoke SUSA for pagination testing


# Install the agent (once)
pip install susatest-agent

# Point at a web URL; SUSA will crawl and paginate
susatest-agent run \
    --url https://shop.example.com/products \
    --personas curious impatient accessibility \
    --output-dir ./susausage-report \
    --generate-scripts

The --personas flag tells SUSA

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