Explorer reports addition
We have added a new Explorer feature to reports, with a timeline scrubber and easy anomaly detection.
Basic pagination testing for offset-based, page-based, and cursor-based API pagination
LoadForge can record your browser, graphically build tests, scan your site with a wizard and more. Sign up now to run your first test.
This guide shows how to test different pagination patterns in APIs. Perfect for validating pagination logic and performance under load.
from locust import task, HttpUser
import random
import json
class PaginationTestUser(HttpUser):
def on_start(self):
# API endpoints with different pagination types
self.endpoints = {
"/api/users": "offset", # offset-based: ?limit=20&offset=40
"/api/products": "page", # page-based: ?page=2&per_page=25
"/api/orders": "cursor" # cursor-based: ?cursor=abc123&limit=30
}
@task(4)
def test_offset_pagination(self):
"""Test offset-based pagination (?limit=20&offset=40)"""
endpoint = "/api/users"
limit = 20
offset = random.choice([0, 20, 40, 60, 100])
params = {"limit": limit, "offset": offset}
with self.client.get(
endpoint,
params=params,
name=f"Offset Pagination - {offset}"
) as response:
if response.status_code == 200:
try:
data = response.json()
# Handle different response formats
if isinstance(data, dict) and "data" in data:
items = data["data"]
total = data.get("total", 0)
print(f"Offset pagination: {len(items)} items, offset {offset}")
# Basic validation
if len(items) > limit:
response.failure(f"Too many items: {len(items)} > {limit}")
else:
items = data if isinstance(data, list) else []
print(f"Offset pagination: {len(items)} items")
except json.JSONDecodeError:
response.failure("Invalid JSON response")
else:
response.failure(f"Pagination failed: {response.status_code}")
@task(3)
def test_page_pagination(self):
"""Test page-based pagination (?page=2&per_page=25)"""
endpoint = "/api/products"
per_page = 25
page = random.choice([1, 2, 3, 4, 5])
params = {"per_page": per_page, "page": page}
with self.client.get(
endpoint,
params=params,
name=f"Page Pagination - {page}"
) as response:
if response.status_code == 200:
try:
data = response.json()
if isinstance(data, dict):
items = data.get("data", data.get("items", []))
current_page = data.get("current_page", data.get("page", page))
total_pages = data.get("total_pages", 0)
print(f"Page pagination: {len(items)} items, page {current_page}")
# Validate page logic
if len(items) > per_page:
response.failure(f"Too many items per page: {len(items)}")
else:
items = data if isinstance(data, list) else []
print(f"Page pagination: {len(items)} items")
except json.JSONDecodeError:
response.failure("Invalid JSON response")
else:
response.failure(f"Page pagination failed: {response.status_code}")
@task(2)
def test_cursor_pagination(self):
"""Test cursor-based pagination (?cursor=abc123&limit=30)"""
endpoint = "/api/orders"
limit = 30
# Test with different cursors (in real use, get from previous responses)
cursor = random.choice([None, "start", "abc123", "xyz789"])
params = {"limit": limit}
if cursor:
params["cursor"] = cursor
with self.client.get(
endpoint,
params=params,
name=f"Cursor Pagination - {cursor or 'start'}"
) as response:
if response.status_code == 200:
try:
data = response.json()
if isinstance(data, dict):
items = data.get("data", data.get("items", []))
next_cursor = data.get("next_cursor")
has_more = data.get("has_more", False)
print(f"Cursor pagination: {len(items)} items, next: {next_cursor}")
# Basic validation
if len(items) > limit:
response.failure(f"Too many items: {len(items)} > {limit}")
else:
items = data if isinstance(data, list) else []
print(f"Cursor pagination: {len(items)} items")
except json.JSONDecodeError:
response.failure("Invalid JSON response")
else:
response.failure(f"Cursor pagination failed: {response.status_code}")
@task(1)
def test_pagination_edge_cases(self):
"""Test pagination edge cases"""
endpoint = random.choice(list(self.endpoints.keys()))
pagination_type = self.endpoints[endpoint]
# Test edge cases based on pagination type
if pagination_type == "offset":
# Test large offset
params = {"limit": 10, "offset": 999999}
edge_case = "large_offset"
elif pagination_type == "page":
# Test page 0
params = {"per_page": 10, "page": 0}
edge_case = "page_zero"
else: # cursor
# Test with empty cursor
params = {"limit": 10, "cursor": ""}
edge_case = "empty_cursor"
with self.client.get(
endpoint,
params=params,
name=f"Edge Case - {edge_case}"
) as response:
if response.status_code in [200, 400, 404]:
print(f"Edge case {edge_case}: {response.status_code}")
else:
print(f"Edge case {edge_case}: unexpected {response.status_code}")