How to Test Pagination on Android (Complete Guide)
Pagination is a core interaction pattern in almost every Android application that displays lists, grids, or feeds. Whether the app loads a timeline of social posts, a catalog of products, or a set of
Why Pagination Testing Matters on Android
Pagination is a core interaction pattern in almost every Android application that displays lists, grids, or feeds. Whether the app loads a timeline of social posts, a catalog of products, or a set of search results, the user expects smooth, predictable navigation between pages. When pagination fails, the consequences are immediate and visible: users see blank screens, experience endless spinners, lose their place, or trigger crashes that lead to negative reviews and uninstalls.
From a quality perspective, pagination touches several layers of the stack:
- Network layer – requests must be formed correctly, handle varying latency, and respect rate limits.
- Data layer – cursors, offsets, or token‑based mechanisms need to be persisted and restored across configuration changes.
- UI layer – RecyclerView, Paging 3, or custom adapters must bind data without leaking views or causing jank.
- Accessibility layer – talkback users rely on announced state changes; missing or incorrect announcements break navigation.
- Security/privacy layer – inadvertent exposure of internal identifiers or tokens in URL parameters can leak data.
Because each layer can introduce a distinct class of defect, a focused pagination test strategy is essential. The following sections walk through a comprehensive test matrix, manual and automated techniques, persona‑driven exploration with SUSA, and production‑only edge cases that often escape scripted checks.
---
Common Pagination Patterns in Android Apps
Before designing tests, it helps to recognize the most frequent implementations you will encounter.
| Pattern | Typical API | UI Component | State Handling |
|---|---|---|---|
| Offset‑based | GET /items?offset=20&limit=20 | RecyclerView with LinearLayoutManager | Offset saved in ViewModel; incremented on each load |
| Cursor‑based | GET /items?cursor=abc123 | PagingDataAdapter (Paging 3) | Cursor stored in PagingSource; refreshed after rotation |
| Token‑based (GraphQL) | query { feed(after: "token") { items … } } | Custom view with DiffUtil | Token retained in ViewModel; cleared on error |
| Infinite scroll with prefetch | GET /feed?page=3 | RecyclerView + setItemPrefetchEnabled(true) | Page number kept in LiveData; prefetch triggers next load |
| Sectioned pagination | Separate endpoints per section | Nested RecyclerViews or StaggeredGridLayoutManager | Each section maintains its own offset/cursor |
Understanding which pattern your app uses informs the test cases you need to write. For example, offset‑based pagination is prone to off‑by‑one errors when the dataset size changes, while cursor‑based pagination can break if the server reuses cursors after a data reset.
---
Test Matrix for Pagination
The table below captures a practical matrix that covers happy paths, error paths, edge cases, accessibility, and security/privacy. Each cell lists a concise description and expected behavior, typical failure symptoms, and a suggested verification method.
| Category | Test ID | Description | Expected Result | Failure Symptom | Verification Method |
|---|---|---|---|---|---|
| Happy Path | HP‑1 | Load first page, scroll to bottom, trigger next load | Second page appended, no duplicates, scroll position smooth | Missing items, duplicate entries, UI jank | Espresso scrollTo + assertOnView |
| HP‑2 | Rotate device while loading second page | Data preserved, loading indicator shown, no loss of state | Data reset, spinner stuck, crash | ADB shell input keyevent KEYCODE_ROTATE + UI check | |
| HP‑3 | Reach last page, attempt to load more | No further request, end‑of‑list indicator shown | Extra request, crash, blank footer | Network mock + assert no new request | |
| Error Path | EP‑1 | Server returns 500 on page 2 | Error snackbar shown, retry button enabled, existing data retained | App crashes, spinner infinite, data lost | Mock server 500 + check snackbar |
| EP‑2 | Network timeout (no response for 10 s) | Timeout message, retry option, previous page still visible | ANR, blank screen, forced close | Use adb shell netem to add delay + assert timeout UI | |
| EP‑3 | Malformed JSON (missing required field) | Graceful degradation, placeholder shown, log error | Parse exception, crash, missing UI | Inject bad JSON via MockWebServer | |
| EP‑4 | Server returns empty list on intermediate page | UI shows “no more items” or placeholder, does not request further | Infinite looping requests, UI stuck | Mock empty response + verify no further calls | |
| Edge Cases | EC‑1 | Rapid successive scrolls (fling) while loading | Requests throttled, UI does not flood network | Duplicate requests, memory spike, UI freeze | Espresso perform fling + network spy |
| EC‑2 | Insertion of new items at top while user is mid‑list | Existing scroll offset preserved, new items appear at top without jump | View jumps, loss of position, duplicated headers | Use ItemAnimator + assert scroll state | |
| EC‑3 | Deletion of items from middle of list | Adapter updates, indices shift correctly, no IndexOutOfBounds | Crash, stale UI, phantom items | Remove item via DB + verify adapter | |
| EC‑4 | Very large page size (e.g., limit=500) | UI remains responsive, memory usage within limits | OOM, dropped frames, ANR | Profile memory with Android Studio Profiler | |
| EC‑5 | Locale change to RTL language while paging | Layout mirrors correctly, scroll direction unchanged | Misaligned items, clipped text, TalkBack misreads | Change language + assert layout direction | |
| Accessibility | AC‑1 | TalkBack user navigates via swipe‑right | Each page load announced (“loading more items”, “page 2 of 5”) | No announcement, confusing hints | Accessibility Test Framework (ATF) + assert spoken feedback |
| AC‑2 | Switch Control user selects “Load more” button | Button receives focus, action performed on activation | Button skipped, focus lost | Use adb shell settings put accessibility … + UIAutomator | |
| AC‑3 | Font size set to largest | Item layouts do not overflow, pagination controls remain tappable | Text cut off, overlapping controls | Change font size + UIAutomator inspector | |
| AC‑4 | High contrast mode enabled | Contrast ratios meet WCAG AA for pagination indicators | Low contrast, invisible indicators | Use developer options + contrast checker | |
| Security/Privacy | SP‑1 | Pagination token appears in logs or network trace | Token is obfuscated or short‑lived, not persisted to disk | Token exposed in plaintext logs, leaked via ADB bugreport | Run adb logcat + grep for token; verify not present |
| SP‑2 | User‑specific data (e.g., email) used as offset parameter | No personal data sent in URL; only opaque identifier | GDPR violation, potential enumeration | Inspect network calls with HttpCanary | |
| SP‑3 | Error responses contain stack traces or internal paths | Server returns generic error message; client does not display details | Internal info leaked to user, possible attack surface | Mock 500 with stack trace + assert UI shows generic message | |
| SP‑4 | Pagination endpoint lacks rate limiting on client side | Client backs off after 429, retries with exponential delay | Hammering server, possible ban, battery drain | Simulate 429 + verify back‑off behavior |
*How to use the matrix*: Pick the rows relevant to your implementation, translate each Test ID into a concrete test case (manual step or automated assertion), and track coverage in your test management tool. The matrix also serves as a checklist for regression runs.
---
Manual Testing Approach
Even with strong automation, manual exploratory testing remains valuable for spotting UX nuances, timing‑dependent glitches, and accessibility quirks that scripts may overlook.
Setup and Environment
- Device preparation – Use a physical device running Android 11+ (API 30) to catch hardware‑specific issues. Enable Developer Options → USB debugging, disable battery optimizations for the test app, and turn off “Don’t keep activities” to avoid artificial state loss.
- Network control – Connect the device to a Wi‑Fi network routed through a tool like Charles Proxy or Mitmproxy. This allows you to throttle latency, drop packets, or inject malformed responses on demand.
- Logging – Run
adb logcat -v threadtime > logcat.txtin a separate terminal to capture runtime exceptions and ANR traces. - UI inspection – Keep Layout Inspector (Android Studio) or UIAutomator Viewer open to verify view hierarchies, especially when checking for duplicated or missing items.
- Accessibility tools – Enable TalkBack, Switch Control, and Font Size scaling from Settings → Accessibility to validate each scenario manually.
Step‑by‑Step Manual Test Procedure
Below is a repeatable flow that covers the majority of the matrix. Adjust the number of pages or dataset size according to your app’s characteristics.
- Launch the app and navigate to the paginated screen (e.g., product list).
- Observe initial load – Confirm that a loading indicator appears, then the first set of items renders without placeholders. Use Layout Inspector to ensure the RecyclerView’s item count matches the expected page size.
- Scroll to the bottom – Perform a slow drag until the RecyclerView signals reaching the last visible item. Verify that a “loading more…” footer or spinner appears and that a network request for the next page is fired (check in Charles).
- Rotate the device – While the second page is loading, press
Ctrl+F11(emulator) or physically rotate the device. Confirm that the loading indicator persists, no data is lost, and after rotation the newly loaded items remain visible. - Introduce network failure – In Charles, map the next‑page endpoint to return a 500 error. Observe that an error snackbar appears with a retry button, that the previously loaded data stays on screen, and that tapping retry re‑issues the request.
- Test empty intermediate page – Return an empty JSON array for page 3. The app should display an “no more items” hint and cease further requests. Ensure the UI does not keep showing a spinner indefinitely.
- Stress with rapid flings – Perform a quick fling from top to bottom repeatedly while the app is still loading. Watch for duplicate requests, memory spikes (via Android Studio Profiler), or UI jank.
- Change locale to RTL – Switch device language to Arabic or Hebrew. Confirm that the list mirrors correctly, that scroll direction feels natural, and that TalkBack announces the proper pagination state.
- Validate accessibility – With TalkBack enabled, swipe right across the list. Each page transition should be announced (e.g., “loading more items”, “page 2”). Activate the “load more” button via double‑tap and confirm the action completes.
- Check security exposure – Disable USB debugging, then run
adb bugreport > report.zip. Extract the report and search for any pagination tokens, user IDs, or internal URLs. Ensure none appear in plain text.
If any step fails, record the exact conditions (device model, OS version, network profile, accessibility setting to reproduce the specific Test ID from the matrix) and file a bug with logs and a short screen‑capture video.
Tools for Manual Verification
- ADB –
adb shell input swipefor precise gestures,adb shell am broadcast -a io.glassbox.feedbackto trigger custom test hooks if you instrumented the app. - Charles / Mitmproxy – Set breakpoints, rewrite responses, simulate latency (
throttleprofile). - Android Studio Profiler – Monitor CPU, memory, and network in real time; look for sudden spikes during rapid scrolling.
- Accessibility Scanner – Automatically highlights low‑contrast or missing content‑description issues while you navigate manually.
- Firebase Test Lab – Run the same manual steps on a matrix of devices to catch device‑specific rendering bugs (especially for RTL layouts).
---
Automated Testing Approaches
Automation provides repeatable regression safety nets. Below are the most effective strategies for Android pagination, ranging from fast unit tests to cross‑device UI automation.
Unit and Integration Tests with Espresso
Espresso shines when you can deterministically control the data layer. Use MockWebServer to enqueue responses and verify UI updates.
@RunWith(AndroidJUnit4::class)
class PaginationEspressoTest {
private val mockWebServer = MockWebServer()
@Before
fun setUp() {
mockWebServer.start()
// Inject the base URL into your app via DI or BuildConfig
val apiService = ServiceGenerator.createApiService(mockWebServer.url("/"))
// Provide the service to your ViewModel (e.g., via Hilt test module)
}
@After
fun tearDown() {
mockWebServer.shutdown()
}
@Test
fun loadFirstAndSecondPage() {
// Page 1
mockWebServer.enqueue(MockResponse()
.setResponseCode(200)
.setBody(loadJsonFixture("page1.json")))
// Page 2
mockWebServer.enqueue(MockResponse()
.setResponseCode(200)
.setBody(loadJsonFixture("page2.json")))
// Launch activity
ActivityScenario.launch(MainActivity::class.java)
// Verify first page items
onView(withId(R.id.recycler_view))
.check(matches(hasDescendant(withText("Item 1"))))
.check(matches(hasDescendant(withText("Item 20"))))
// Scroll to bottom to trigger next load
onView(withId(R.id.recycler_view))
.perform(swipeDown()) // assuming linear layout manager
.perform(scrollToPosition(19)) // last item of page 1
// Verify loading indicator appears
onView(withId(R.id.progress_bar))
.check(matches(isDisplayed()))
// Wait for second page
onView(withId(R.id.recycler_view))
.check(matches(hasDescendant(withText("Item 21"))))
.check(matches(hasDescendant(withText("Item 40"))))
// Ensure no extra request was made
mockWebServer.takeSequence() // should contain exactly 2 requests
}
}
Key points:
- Use
IdlingResource(orCountingIdlingResource) to synchronize with asynchronous paging loads. - Verify that the adapter’s
getItemCount()increases as expected. - Assert that no duplicate view holders are created (you can spy on the adapter’s
onBindViewHolder).
UI Automation with UIAutomator
UIAutomator works well for black‑box tests where you cannot modify the app source (e.g., testing a third‑party SDK or a release APK). It interacts with the accessibility layer, making it inherently sensitive to accessibility bugs.
public class PaginationUiAutomatorTest {
private UiDevice device;
@Before
public void setUp() throws Exception {
device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
device.pressHome();
// Launch the app via its package name
Context ctx = InstrumentationRegistry.getInstrumentation().getTargetContext();
Intent intent = ctx.getPackageManager()
.getLaunchIntentForPackage("com.example.myapp");
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
ctx.startActivity(intent);
device.wait(Until.hasObject(By.pkg("com.example.myapp").depth(0)), 5000);
}
@Test
public void testPaginationWithNetworkError() throws Exception {
// Set up a network proxy that will return 500 for page 2
// (Assume you have a helper that configures the device's Wi‑Fi proxy)
NetworkUtils.setProxyAndThrottle("192.168.1.100", 8888, 5000); // 5 s latency
// Wait for first page to load
UiObject2 list = device.wait(Until.findObject(By.res("com.example.myapp", "recycler_view")), 10000);
assertNotNull(list);
// Scroll to bottom
for (int i = 0; i < 5; i++) {
list.swipe(Swipe.DOWN, 0.5f);
SystemClock.sleep(500);
}
// Verify error snackbar appears
UiObject2 snackbar = device.wait(Until.findObject(By.textContains("Failed to load")), 8000);
assertNotNull(snackbar);
// Tap retry
UiObject2 retry = device.wait(Until.findObject(By.res("com.example.myapp", "btn_retry")), 5000);
retry.click();
// After retry, second page should appear
device.wait(Until.findObject(By.text("Item 21")), 10000);
assertTrue(device.findObject(By.text("Item 21")).exists());
}
}
UIAutomator tests are slower than Espresso but give you confidence that the app behaves correctly under real system conditions (e.g., when TalkBack is on, or when the device is in split‑screen mode).
Leveraging Appium for Cross‑Device Testing
If you need to run the same test suite on multiple device configurations (different OS versions, screen sizes, or locales) without maintaining separate instrumentation code, Appium offers a convenient bridge.
# appium-capabilities.yaml
capabilities:
- platformName: Android
automationName: UiAutomator2
deviceName: Pixel_4_API_30
app: /path/to/app-debug.apk
appPackage: com.example.myapp
appActivity: .MainActivity
locale: en
language: US
orientation: PORTRAIT
- platformName: Android
automationName: UiAutomator2
deviceName: Samsung_Galaxy_S21_API_33
app: /path/to/app-debug.apk
appPackage: com.example.myapp
appActivity: .MainActivity
locale: ar
language: AE
orientation: LANDSCAPE
A sample Appium test in JavaScript (using WebDriverIO) that validates pagination:
describe('Pagination flow', () => {
it('should load second page after scrolling', async () => {
// Wait for RecyclerView
const recycler = await $('android=new UiSelector().resourceId("com.example.myapp:id/recycler_view")');
await recycler.waitForExist({ timeout: 10000 });
// Grab first item text
const firstItem = await $('android=new UiSelector().resourceId("com.example.myapp:id/item_text").instance(0)');
const firstText = await firstItem.getText();
expect(firstText).toMatch(/Item \d+/);
// Scroll to bottom (perform a swipe)
await driver.touchPerform([
{ action: 'press', options: { x: 500, y: 1500 } },
{ action: 'wait', options: { ms: 200 } },
{ action: 'moveTo', options: { x: 500, y: 500 } },
{ action: 'release' }
]);
// Wait for loading indicator to disappear
const loader = await $('android=new UiSelector().resourceId("com.example.myapp:id/progress_bar")');
await loader.waitForExist({ reverse: true, timeout: 15000 });
// Verify second page items appear
const secondItem = await $('android=new UiSelector().resourceId("com.example.myapp:id/item_text").instance(20)');
const secondText = await secondItem.getText();
expect(secondText).toMatch(/Item \d+/);
});
});
Run the test against the capability matrix with:
npx wdio run wdio.conf.js --spec ./test/pagination.spec.js
Appium’s strength lies in executing the same script on a fleet of real devices or emulators, which helps surface device‑specific pagination quirks (e.g., OEM‑specific RecyclerView bugs).
Leveraging SUSA for Autonomous Exploration
SUSA (the autonomous QA platform) can be pointed at your APK or a web‑view wrapper and left to explore the app without any test scripts. It builds a session‑based knowledge graph of screens, transitions, and dead ends, then applies a set of persona‑driven behavior models to exercise the UI in ways that manual testers might overlook.
- How it helps pagination – SUSA automatically tries varied scroll velocities, rapid flings, orientation changes mid‑load, and accessibility‑focused navigation (TalkBack, Switch Control). Because it maintains a per‑session memory of which endpoints have returned empty lists or errors, it can detect scenarios like “empty intermediate page causing infinite reload” that a static script would never consider unless explicitly programmed.
- Persona relevance – The “impatient” persona performs quick, repeated flings; the “elderly” persona uses slower gestures and frequently taps the “load more” button; the “accessibility” persona relies on TalkBack swipe gestures; the “adversarial” persona deliberately sends malformed inputs (e.g., huge page size parameters) to probe limits.
- Outcome – After a run, SUSA produces a PASS/FAIL verdict for each discovered flow (login, signup, pagination, etc.), highlights any crashes, ANRs, accessibility violations, and security hints (e.g., tokens appearing in logcat). It also exports the explored paths as Appium (Android) + Playwright (Web) regression scripts, giving you a concrete starting point for automated test suites.
To invoke SUSA on a local build:
pip install susatest-agent
susatest-agent run \
--apk ./app-release.apk \
--device-id emulator-5554 \
--personas curious impatient elderly accessibility \
--output-dir ./susausage-report
The resulting report contains a dedicated “Pagination” section with screenshots, network traces, and any detected defects. While SUSA is not a replacement for unit test framework, it complements your existing test suite by surfacing edge cases that only manifest under realistic, varied user behavior.
---
Persona‑Driven Exploration and Its Benefits
Personas encode distinct interaction patterns, motivations, and limitations. When applied to pagination testing, they expose bugs that are invisible to a single “happy‑path” script.
Persona Profiles Relevant to Pagination
| Persona | Core Traits | Typical Interaction with Pagination |
|---|---|---|
| Curious | Explores every UI element, taps unknown icons, reads tooltips | May long‑press items to open context menus while scrolling, triggering unexpected adapter updates |
| Impatient | Performs rapid gestures, dislikes waiting, retries quickly | Frequently flings, taps “load more” before spinner disappears, causing duplicate requests |
| Novice | Relies on visual cues, avoids gestures they don’t understand | May miss the infinitescroll indicator, rely exclusively on explicit “Load more” button |
| Elderly | Slower motor response, prefers larger touch targets, often uses accessibility services | May double‑tap inadvertently, need larger pagination controls, may trigger TalkBack announcements frequently |
| Accessibility | Depends on screen reader, switch control, or voice commands | Relies on spoken state changes; missing announcements break their sense of progress |
| Power user | Uses shortcuts, expects high performance, often changes device orientation | May rotate device while list is loading, expects state preservation and no UI jank |
| Adversarial | Attempts to break the system, sends extreme values, crafts malformed requests | May manipulate pagination parameters via accessibility service or network proxy to trigger OOM or crashes |
How SUSA Simulates Personas
SUSA’s engine includes a set of behavior modules that modify the base exploration algorithm:
- Gesture velocity – Impatient persona uses a higher swipe speed distribution; Elderly uses slower, more deliberate swipes.
- Decision thresholds – Curious persona has a higher probability to long‑press or open overflow menus on each item.
- Assistive tech activation – Accessibility persona automatically enables TalkBack and configures swipe‑right navigation speed.
- Error injection – Adversarial persona intermittently tamper with query parameters (e.g.,
limit=999999) or injects corrupt JSON via a local proxy.
These modifiers are applied on‑the‑fly as SUSA traverses the app, meaning a single run can generate dozens of distinct pagination scenarios without any test author needing to anticipate them.
Real‑World Bugs Found Only via Persona Exploration
During a recent internal audit of a media‑streaming app, SUSA uncovered three pagination‑related defects that escaped both Espresso and manual test plans:
- Impatient‑induced duplicate network calls – When the user flings twice within 300 ms while the first page is still loading, the app’s
RecyclerView.OnScrollListenerfired twice, causing two simultaneous page‑2 requests. The backend returned identical data, leading to visible duplicate rows in the list. The fix added a debouncing flag in the ViewModel. - Elderly‑triggered TalkBack announcement loss – With TalkBack enabled, the app only announced “loading more” when the
ProgressBarchanged visibility fromGONEtoVISIBLE. However, when the user slowly scrolled (as the Elderly persona does), the progress bar never fully disappeared before the next load began, so the announcement was skipped. The solution was to announce based on the data source’sloadStaterather than UI visibility. - Adversarial‑triggered OOM via huge page size – By manipulating the network proxy to return
limit=50000(instead of the default 20), the app attempted to inflate 50 k view holders in a single layout pass, exceeding the heap limit on low‑end devices. The fix added server‑side validation and a client‑side clamp (limit = Math.min(limit, 100)).
These examples illustrate why persona‑driven exploration is a valuable complement to scripted tests: it surfaces timing‑sensitive, accessibility‑sensitive, and boundary‑condition bugs that are otherwise hard to anticipate.
---
Production‑Only Edge Cases
Certain pagination flaws only manifest after the app has been released to a broad audience, where device heterogeneity, real‑world network conditions, and user behavior patterns diverge from lab environments.
Network Variability and Caching
- Intermittent 304 Not Modified – Some backends return
304with an empty body when the client sends anIf‑None‑Matchheader. If the paging library treats a 304 as “no data”, it may prematurely stop fetching further pages. - Cache stampede – After a network loss, multiple UI components (e.g., a swipe‑refresh and the infinite scroll listener) may both attempt to reload the same page, causing a thundering herd of requests.
- DNS rebinding attacks – A malicious Wi‑Fi network could resolve the API domain to an internal IP, leaking pagination tokens to an unauthorized host.
Mitigation: Use a centralized paging repository that deduplicates requests based on the current LoadState. Validate HTTP status codes explicitly; treat 304 as a successful reload but keep the existing data set. Pin API endpoints via network security config.
Background Threading and Configuration Changes
- ViewModel scope mismatch – If the PagingData is collected in a UI‑scoped
viewModelScopebut the UI is destroyed during a rotation, the flow may be cancelled, leaving a dangling network call that later tries to update a detached adapter. - Executor leakage – Custom
Executorused for paging work not shut down ononClearedcan accumulate threads, eventually exhausting the system limit.
Mitigation: Collect paging flows in lifecycleScope tied to the activity/fragment lifecycle, or use repeatOnLifecycle(Lifecycle.State.STARTED) to auto‑cancel on stop. Ensure any custom executors are shut down in the ViewModel’s onCleared.
Large Data Sets and Memory Pressure
- DiffUtil overload – When the list size grows beyond a few thousand items, DiffUtil’s calculation of item changes can exceed the UI thread’s frame budget, causing dropped frames.
- Bitmap caching in item views – If each list item loads a high‑resolution thumbnail and holds onto it in memory, scrolling quickly can trigger GC pauses and occasional OOM kills.
Mitigation:
- Enable
setInitialLoadSizeandsetPrefetchDistancein the PagingConfig to limit the amount of data held in memory. - Use
ListAdapterwithsubmitListwhich leverages DiffUtil on a background thread. - Load thumbnails with libraries like Glide or Cooley that downsample based on
ImageViewdimensions and clear references inonViewRecycled.
Localization and RTL Layouts
- Hard‑coded paddings – Some layouts use
marginStart/marginEndincorrectly, causing clipped content when the system switches to RTL. - Locale‑specific number formatting – Page indicators like “Page 3 of 12” may break if the app concatenates strings without using
QuantityStringor proper formatting, leading to misplaced digits in languages with different numeral shapes.
Mitigation: Run the app with adb shell setprop persist.sys.language ar && adb shell setprop persist.sys.country EG and verify every screen with Layout Inspector. Use Android’s NumberFormat.getInstance(Locale) for any numeric UI.
---
Checklist for Pagination Testing
| ✅ Item | Description | How to Verify |
|---|---|---|
| HP‑1 | First page loads correctly, no placeholders | Visual check + RecyclerView item count |
| HP‑2 | State survives rotation | Rotate during load, confirm data persists |
| HP‑3 | No request beyond last page | Mock final page → verify no further network call |
| EP‑1 | Graceful handling of 5xx errors | Mock 500 → error UI + retry works |
| EP‑2 | Timeout handling and retry | Simulate delay → timeout UI + retry |
| EP‑3 | Malformed JSON does not crash | Inject bad JSON → placeholder/log |
| EP‑4 | Empty intermediate page stops pagination | Mock empty array → “no more items” shown |
| EC‑1 | Rapid flings do not flood network | Perform quick swipes → spy on request count |
| EC‑2 | Insertions at top preserve scroll position | Add items via DB → verify offset unchanged |
| EC‑3 | Deletions update adapter without crash | Remove item → check IndexOutOfBounds absent |
| EC‑4 | Large page size stays within memory budget | Set limit high → profile memory, avoid OOM |
| EC‑5 | RTL layout mirrors correctly, TalkBack functional | Switch language → inspect layout direction, announcements |
| AC‑1 | TalkBack announces page loads | Enable TalkBack → listen for “loading more”, “page X” |
| AC‑2 | Switch Control can activate load more | Enable Switch Control → verify focus & action |
| AC‑3 | Font scaling does not break item layout | Set largest font → UI intact |
| AC‑4 | Contrast meets WCAG AA for pagination controls | Use Accessibility Scanner or Contrast Checker |
| SP‑1 | No token leakage in logs or bugreport | adb logcat + adb bugreport → grep for tokens |
| SP‑2 | No personal data in pagination URL | Inspect network calls → no emails/IDs in query |
| SP‑3 |
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