Explorer reports addition
We have added a new Explorer feature to reports, with a timeline scrubber and easy anomaly detection.
Basic search API testing for text search, filtering, and result validation
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 search APIs with common operations like text search, filtering, faceted search, and pagination.
from locust import task, HttpUser
import random
import json
class SearchTestUser(HttpUser):
def on_start(self):
# Search terms for testing
self.search_terms = [
"python", "api", "testing", "performance", "database",
"tutorial", "guide", "example", "documentation", "development"
]
# Filter options
self.filters = {
"category": ["technology", "business", "science", "sports"],
"date_range": ["today", "week", "month", "year"],
"type": ["article", "video", "document", "image"]
}
# API endpoints
self.search_endpoints = {
"search": "/api/search",
"suggest": "/api/search/suggest",
"autocomplete": "/api/search/autocomplete"
}
@task(4)
def test_basic_search(self):
"""Test basic text search"""
query = random.choice(self.search_terms)
search_params = {
"q": query,
"limit": random.choice([10, 20, 50]),
"offset": random.choice([0, 10, 20])
}
with self.client.get(
self.search_endpoints["search"],
params=search_params,
name="Basic Search"
) as response:
if response.status_code == 200:
try:
results = response.json()
# Handle different response formats
if isinstance(results, dict):
items = results.get("results", results.get("hits", []))
total = results.get("total", len(items))
took = results.get("took", 0)
else:
items = results if isinstance(results, list) else []
total = len(items)
took = 0
print(f"Search '{query}': {len(items)} results ({took}ms)")
except json.JSONDecodeError:
response.failure("Invalid JSON response")
else:
response.failure(f"Search failed: {response.status_code}")
@task(3)
def test_filtered_search(self):
"""Test search with filters"""
query = random.choice(self.search_terms)
category = random.choice(self.filters["category"])
date_range = random.choice(self.filters["date_range"])
search_params = {
"q": query,
"category": category,
"date": date_range,
"limit": 20
}
with self.client.get(
self.search_endpoints["search"],
params=search_params,
name="Filtered Search"
) as response:
if response.status_code == 200:
try:
results = response.json()
if isinstance(results, dict):
items = results.get("results", results.get("hits", []))
facets = results.get("facets", {})
print(f"Filtered search '{query}' in {category}: {len(items)} results")
# Validate filters were applied
if facets:
print(f"Available facets: {list(facets.keys())}")
else:
items = results if isinstance(results, list) else []
print(f"Filtered search: {len(items)} results")
except json.JSONDecodeError:
response.failure("Invalid JSON response")
else:
response.failure(f"Filtered search failed: {response.status_code}")
@task(2)
def test_search_suggestions(self):
"""Test search suggestions/autocomplete"""
partial_query = random.choice(self.search_terms)[:3] # First 3 characters
suggest_params = {
"q": partial_query,
"limit": 10
}
with self.client.get(
self.search_endpoints["suggest"],
params=suggest_params,
name="Search Suggestions"
) as response:
if response.status_code == 200:
try:
suggestions = response.json()
if isinstance(suggestions, dict):
items = suggestions.get("suggestions", suggestions.get("items", []))
else:
items = suggestions if isinstance(suggestions, list) else []
print(f"Suggestions for '{partial_query}': {len(items)} items")
except json.JSONDecodeError:
response.failure("Invalid JSON response")
else:
response.failure(f"Search suggestions failed: {response.status_code}")
@task(2)
def test_advanced_search(self):
"""Test advanced search with multiple parameters"""
search_data = {
"query": {
"text": random.choice(self.search_terms),
"fields": ["title", "content", "tags"]
},
"filters": {
"type": random.choice(self.filters["type"]),
"category": random.choice(self.filters["category"])
},
"sort": random.choice(["relevance", "date", "popularity"]),
"pagination": {
"page": random.randint(1, 3),
"per_page": 20
}
}
with self.client.post(
self.search_endpoints["search"],
json=search_data,
name="Advanced Search"
) as response:
if response.status_code == 200:
try:
results = response.json()
if isinstance(results, dict):
items = results.get("results", results.get("hits", []))
total = results.get("total", len(items))
page = results.get("page", 1)
print(f"Advanced search: {len(items)} results, page {page}")
else:
items = results if isinstance(results, list) else []
print(f"Advanced search: {len(items)} results")
except json.JSONDecodeError:
response.failure("Invalid JSON response")
else:
response.failure(f"Advanced search failed: {response.status_code}")
@task(1)
def test_empty_search(self):
"""Test search with empty or invalid queries"""
empty_queries = ["", " ", "xyz123nonexistent", "!@#$%^&*()"]
query = random.choice(empty_queries)
search_params = {
"q": query,
"limit": 10
}
with self.client.get(
self.search_endpoints["search"],
params=search_params,
name="Empty/Invalid Search"
) as response:
if response.status_code in [200, 400]:
try:
results = response.json()
if isinstance(results, dict):
items = results.get("results", results.get("hits", []))
error = results.get("error")
if error:
print(f"Search error for '{query}': {error}")
else:
print(f"Empty search '{query}': {len(items)} results")
else:
items = results if isinstance(results, list) else []
print(f"Empty search: {len(items)} results")
except json.JSONDecodeError:
print(f"Non-JSON response for empty search")
else:
print(f"Empty search returned: {response.status_code}")
@task(1)
def test_search_aggregations(self):
"""Test search with aggregations/facets"""
query = random.choice(self.search_terms)
agg_params = {
"q": query,
"facets": "category,type,date",
"limit": 20
}
with self.client.get(
self.search_endpoints["search"],
params=agg_params,
name="Search with Aggregations"
) as response:
if response.status_code == 200:
try:
results = response.json()
if isinstance(results, dict):
items = results.get("results", results.get("hits", []))
aggregations = results.get("aggregations", results.get("facets", {}))
print(f"Aggregated search '{query}': {len(items)} results")
if aggregations:
print(f"Aggregations: {list(aggregations.keys())}")
else:
items = results if isinstance(results, list) else []
print(f"Aggregated search: {len(items)} results")
except json.JSONDecodeError:
response.failure("Invalid JSON response")
else:
response.failure(f"Aggregated search failed: {response.status_code}")