How to Write Test Cases for Search Functionality (With Examples)
How to Write Test Cases for Search Functionality (With Examples)
How to Write Test Cases for Search Functionality (With Examples)
Search is one of the most used features in any application, yet it is also one of the most fault‑prone. A single missed edge case can lead to irrelevant results, security holes, or a frustrating user experience that drives users away. This guide walks you through a complete process for creating high‑signal test cases for search functionality, from anatomy to prioritization, manual and automated execution, and finally a practical checklist you can bookmark. Throughout the article you will find a worked test matrix with more than twenty examples, code snippets that show how to automate the checks, and notes on how autonomous exploration tools like SUSA can supplement manual effort.
How to Write Test Cases for Search Functionality (With Examples): Test Case Anatomy
Every test case needs a clear structure so that reviewers, developers, and automation engineers can understand it at a glance. The following elements are essential:
- Test Case ID – a unique identifier that enables traceability to requirements and test management tools.
- Title – a short, descriptive phrase that summarizes the scenario.
- Preconditions – the state the system must be in before the test starts (e.g., index populated with a known dataset, user logged in).
- Test Data – the exact input values, files, or environment variables required.
- Steps – an ordered list of actions the tester or script will perform. Each step should be atomic and unambiguous.
- Expected Result – the observable outcome that defines pass or fail (e.g., list of results, error message, performance metric).
- Postconditions – any cleanup needed to return the system to a stable state (e.g., delete temporary index entries).
- Priority / Severity – helps with test execution ordering and defect triage.
- Tags – labels such as
positive,negative,performance,security,accessibilitythat enable filtering.
Elements of a Test Case
A well‑written test case avoids vague language. Instead of “enter something in the search box”, write “type the string red shoes into the search input field with the placeholder Search products…”. This eliminates interpretation differences between manual testers and automated scripts. When you write the expected result, be explicit about both the data and the UI state. For example, “the results pane displays exactly three product cards, each containing the title Red Running Shoes, price $49.99, and an Add to Cart button that is enabled”.
Writing Clear Steps and Expected Results
Steps should follow the given‑when‑then pattern implicitly:
- Given – preconditions and test data are satisfied.
- When – the user performs the action (e.g., clicks the search button).
- Then – the system exhibits the expected behavior.
Avoid combining multiple actions in a single step; splitting them makes debugging easier when a step fails. For expected results, include both positive assertions (what should appear) and negative assertions (what should not appear). For a negative test case you might state, “no results are shown and an inline message No products match your query is displayed”.
How to Write Test Cases for Search Functionality (With Examples): Positive Test Cases
Positive test cases verify that the search engine returns the correct results for valid input. Below is a representative set organized by theme. Each row can be copied directly into a test management tool.
Basic Valid Queries
| ID | Preconditions | Steps | Expected Result |
|---|---|---|---|
| S01 | Product index contains items: “Apple iPhone 12”, “Samsung Galaxy S21”, “Google Pixel 5”. No active filters. | 1. Tap the search bar. 2. Enter iPhone. 3. Press the search icon. | Results list shows only the Apple iPhone 12 card. No other items appear. |
| S02 | Same index as S01. | 1. Tap search bar. 2. Enter galaxy s. 3. Press search. | Results list shows only the Samsung Galaxy S21 card. |
| S03 | Index includes a product with SKU ABC-123. | 1. Tap search bar. 2. Enter ABC-123. 3. Press search. | Exactly one result with SKU ABC-123 is displayed. |
| S04 | Index contains 1000 products; none match the query zzz. | 1. Enter zzz. 2. Press search. | No results pane shows the message No products match your query. |
| S05 | Index contains products with prices ranging from $5 to $500. | 1. Enter under 100. 2. Press search. | Results list includes only products whose price ≤ $100, sorted by relevance. |
Synonyms and Stemming
| ID | Preconditions | Steps | Expected Result |
|---|---|---|---|
| S06 | Index contains “running shoes”, “jogging sneakers”, “athletic footwear”. Synonym map: run ↔ jog. Stemming enabled for -ing. | 1. Enter jog. 2. Press search. | Results include “running shoes”, “jogging sneakers”, “athletic footwear”. |
| S07 | Same index. | 1. Enter runs. 2. Press search. | Results include “running shoes” (stemmed to run). |
| S08 | Index contains “cat”, “cats”, “caterpillar”. | 1. Enter cat. 2. Press search. | Results include “cat” and “cats” but not “caterpillar” (if tokenization respects word boundaries). |
| S09 | Index contains “color” (US) and “colour” (UK). Synonym map includes both spellings. | 1. Enter colour. 2. Press search. | Results include both US and UK spelling variants. |
| S10 | Index contains “e‑mail” and “email”. | 1. Enter email. 2. Press search. | Results include both hyphenated and non‑hyphenated forms. |
Filters and Sorting
| ID | Preconditions | Steps | Expected Result |
|---|---|---|---|
| S11 | Index includes products with attributes: category (Electronics, Books), brand (Sony, Apple), price. | 1. Enter headphones. 2. Open filter panel. 3. Select category Electronics. 4. Apply. | Results list shows only headphones tagged as Electronics. |
| S12 | Same as S11. | 1. After applying category filter, select brand Sony. 2. Apply. | Results list shows only Sony headphones. |
| S13 | Index contains 50 products priced between $10 and $200. | 1. Enter speaker. 2. Open sort dropdown. 3. Choose Price: Low to High. 4. Apply. | Results are displayed in ascending price order; first item ≤ $20, last item ≥ $180. |
| S14 | Same as S13. | 1. Choose Price: High to Low. 2. Apply. | Results are displayed in descending price order. |
| S15 | Index includes timestamped blog posts. | 1. Enter AI. 2. Open date filter. 3. Select Last 30 days. 4. Apply. | Only posts published within the last 30 days containing AI appear. |
| S16 | Index contains documents with language tags (en, es, fr). | 1. Enter manual. 2. Open language filter. 3. Select es. 4. Apply. | Only Spanish language documents are shown. |
How to Write Test Cases for Search Functionality (With Examples): Negative and Edge Cases
Negative tests confirm that the system handles invalid or unexpected input gracefully. Edge cases push the limits of length, character sets, and concurrency.
Empty and Whitespace Queries
| ID | Preconditions | Steps | Expected Result |
|---|---|---|---|
| N01 | Index populated. Search bar empty. | 1. Tap search icon without typing. | Inline validation shows Please enter a search term. No results request is sent to backend. |
| N02 | Same. | 1. Enter a single space ( ). 2. Tap search. | Same validation as N01 (or trimmed to empty and shows same message). |
| N03 | Same. | 1. Enter three spaces ( ). 2. Tap search. | Validation error appears; no network call. |
| N04 | Same. | 1. Enter a tab character (\t). 2. Tap search. | Treated as whitespace; validation error appears. |
| N05 | Same. | 1. Enter a newline (\n). 2. Tap search. | Validation error appears. |
Special Characters and SQL Injection
| ID | Preconditions | Steps | Expected Result |
|---|---|---|---|
| N06 | Index contains product O'Reilly. | 1. Enter O'Reilly. 2. Tap search. | Results show the product; no error or unexpected behavior. |
| N07 | Same. | 1. Enter '; DROP TABLE products;--. 2. Tap search. | System treats the string as literal search term; returns zero results (or appropriate no‑match message). No database error is exposed. |
| N08 | Same. | 1. Enter . 2. Tap search. | Input is escaped or stripped; no script executes in the results page. |
| N09 | Same. | 1. Enter %%%. 2. Tap search. | System returns zero results or a message indicating no matches; no crash. |
| N10 | Same. | 1. Enter a string of 1000 ampersands (&&&&…). 2. Tap search. | System handles the input without throwing an exception; response time stays within SLA. |
Very Long Queries
| ID | Preconditions | Steps | Expected Result |
|---|---|---|---|
| L01 | Index contains typical product names (max length 100 chars). | 1. Generate a random string of 2000 characters. 2. Paste into search bar. 3. Tap search. | Backend returns a 400 Bad Request or a UI message Search term too long (max 100 characters). No crash. |
| L02 | Same. | 1. Enter exactly 100 characters (the limit). 2. Tap search. | Query is accepted and processed; results returned according to relevance. |
| L03 | Same. | 1. Enter 101 characters. 2. Tap search. | Validation error appears; no backend call. |
| L04 | Same. | 1. Enter a string of 5000 Unicode emojis. 2. Tap search. | Same length validation error as L01/L03. |
| L05 | Same. | 1. Enter a mix of 80 Latin characters and 20 CJK characters. 2. Tap search. | Query accepted if total code units ≤ limit; results returned. |
Unicode and Emoji
| ID | Preconditions | Steps | Expected Result |
|---|---|---|---|
| U01 | Index contains product named 😀 Smiling Face T‑Shirt. | 1. Enter 😀. 2. Tap search. | Result shows the T‑Shirt. |
| U02 | Same. | 1. Enter smiling face. 2. Tap search. | Result shows the T‑Shirt (if synonym/emoji mapping enabled). |
| U03 | Index contains product café. | 1. Enter cafe (without accent). 2. Tap search. | Depending on normalization, either returns the café product (if accent‑insensitive) or shows no match (if accent‑sensitive). Document the behavior. |
| U04 | Same. | 1. Enter é. 2. Tap search. | Returns café if diacritic‑sensitive matching is enabled. |
| U05 | Index contains product 👍 Thumbs Up Sticker. | 1. Enter 👍. 2. Tap search. | Returns the sticker. |
| U06 | Same. | 1. Enter thumbs up. 2. Tap search. | Returns the sticker if mapping configured; otherwise no match. |
| U07 | Index contains product with Japanese title こんにちは. | 1. Enter こんにちは. 2. Tap search. | Returns the product. |
| U08 | Same. | 1. Enter konnichiwa (romaji). 2. Tap search. | Behavior depends on transliteration support; document outcome. |
Data Setup and Test Environment for Search Testing
Reliable test execution depends on a known, repeatable state of the search index and any external services it relies on (e.g., recommendation engines, personalization APIs). The following practices help you create a stable foundation.
Populating Test Index
Before each test suite run, reset the index to a baseline dataset. Use scripts that:
- Delete the existing index (or collection).
- Load a CSV or JSON fixture containing a controlled set of records with known fields (title, description, price, categories, timestamps, language tags).
- Optionally inject specific edge‑case records (e.g., very long titles, special characters, emojis).
- Commit the index and verify document count matches the fixture.
A sample Bash snippet using Elasticsearch’s _bulk API:
#!/usr/bin/env bash
INDEX="test_search"
curl -X DELETE "http://localhost:9200/$INDEX?pretty"
curl -X PUT "http://localhost:9200/$INDEX" -H 'Content-Type: application/json' -d'
{
"settings": {
"number_of_shards": 1,
"number_of_replicas": 0,
"analysis": {
"analyzer": {
"default": {
"type": "custom",
"tokenizer": "standard",
"filter": ["lowercase", "asciifolding"]
}
}
}
}
}'
# Load fixtures
curl -X POST "http://localhost:9200/$INDEX/_bulk?pretty" -H 'Content-Type: application/json' --data-binary @fixtures.json
Mocking External Services
If your search ranking depends on a recommendation microservice or a real‑time personalization engine, replace those calls with deterministic stubs during testing. Tools like WireMock (Java) or msw (JavaScript) let you define fixed responses:
// msw handler for personalization endpoint
import { rest } from 'msw'
export const handlers = [
rest.get('/api/personalization/:userId', (req, res, ctx) => {
return res(
ctx.json({ boostFactor: 1.0, excludedCategories: [] })
)
})
]
Using SUSA for Autonomous Exploration (mention SUSA here)
SUSA can be pointed at a running build of your application to exercise search without writing scripts. After you upload the APK or provide the web URL, SUSA’s autonomous agent will:
- Generate varied search queries based on its persona models (curious, impatient, power user, etc.).
- Attempt to submit empty, long, and special‑character strings.
- Follow result links, apply filters, and sort options to discover hidden navigation paths.
- Log any crashes, ANRs, or WCAG violations that occur during these interactions.
You can invoke SUSA from CI with:
pip install susatest-agent
susatest run --app ./app-debug.apk --personas all --output susa-report.json
The report feeds into your test case prioritization: any flow that SUSA flags as flaky or error‑prone becomes a candidate for additional manual or automated test cases.
Prioritization and Traceability to Requirements
Not all test cases carry equal weight. Use a risk‑based matrix that combines likelihood of failure with impact on users. Link each test case to a specific requirement or user story so that coverage gaps are visible.
Risk‑Based Prioritization
Define two scales:
- Likelihood (Rare, Unlikely, Possible, Likely, Certain) – based on historical defect data, complexity of the feature, and input variability.
- Impact (Low, Medium, High, Critical) – based on user‑facing consequences (e.g., showing wrong products vs. exposing data).
Calculate a priority score (e.g., Likelihood × Impact) and sort. Example mapping:
| Likelihood \ Impact | Low | Medium | High | Critical |
|---|---|---|---|---|
| Rare | L1 | L2 | L3 | L4 |
| Unlikely | L2 | L3 | L4 | L5 |
| Possible | L3 | L4 | L5 | L6 |
| Likely | L4 | L5 | L6 | L7 |
| Certain | L5 | L6 | L7 | L8 |
Assign numeric values (1‑8) and treat ≤ 3 as Low, 4‑5 as Medium, 6‑7 as High, ≥ 8 as Critical for execution order.
Linking Test Cases to User Stories
In your test management tool (e.g., Jira, TestRail, Zephyr), add a field “Related Requirement” that stores the issue key of the user story. For search, typical stories might be:
- US‑101: As a shopper, I can type a product name and see matching items.
- US‑102: As a shopper, I can filter results by category and brand.
- US‑103: As a shopper, I receive a helpful message when no results are found.
- US‑104: As a security‑conscious user, I expect malicious input to be sanitized.
When you create a test case, copy the story ID into the “Related Requirement” field. This enables traceability reports that show, for each story, the percentage of test cases that are automated, passed, or pending.
Manual vs Automated Approaches for Search Test Cases
Both manual exploratory testing and automated regression suites have roles. The decision matrix below helps you decide where to invest effort.
| Factor | Manual Testing | Automated Testing |
|---|---|---|
| Exploratory value | High – testers can try unexpected queries, observe UI subtleties. | Low – follows pre‑defined scripts. |
| Regression safety | Medium – depends on tester diligence. | High – runs on every build, catches regressions fast. |
| Setup time | Low – just a device or browser. | Medium – requires test framework, data seeding, CI integration. |
| Maintenance | Low – no code to maintain. | Medium – scripts need updates when locators or APIs change. |
| Performance/Load | Limited – manual repetition is tedious. | Easy to scale with tools like JMeter or k6. |
| Accessibility checks | Possible with screen‑reader testing, but inconsistent. | Automated axe‑core or similar can be integrated. |
When to Automate
Automate test cases that:
- Are executed on every commit (e.g., basic positive queries, validation of empty input).
- Have deterministic outcomes (no randomness).
- Involve repetitive data setup (e.g., loading the same fixture).
- Cover security or compliance checks (e.g., SQL‑injection payloads).
Leave to manual testing:
- New feature exploration where the exact expected behavior is still evolving.
- Complex UX flows that depend on subjective judgments (e.g., relevance ranking “feels right”).
- Ad‑hoc checks for locale‑specific linguistic nuances that are hard to encode.
Sample Automated Script (Appium + Playwright)
Below is a compact example that verifies a positive search scenario on an Android app using Appium, then the same scenario on a web version using Playwright. Adjust capabilities and selectors to match your product.
#### Appium (Android) – Java
import io.appium.java_client.AppiumDriver;
import io.appium.java_client.MobileElement;
import io.appium.java_client.android.AndroidDriver;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.openqa.selenium.By;
import org.openqa.selenium.remote.DesiredCapabilities;
import java.net.URL;
import java.util.List;
public class SearchTest {
private AppiumDriver<MobileElement> driver;
@Before
public void setUp() throws Exception {
DesiredCapabilities caps = new DesiredCapabilities();
caps.setCapability("deviceName", "Pixel_4_API_33");
caps.setCapability("platformName", "Android");
caps.setCapability("appPackage", "com.example.shop");
caps.setCapability("appActivity", ".MainActivity");
caps.setCapability("automationName", "UiAutomator2");
driver = new AndroidDriver<>(new URL("http://localhost:4723/wd/hub"), caps);
}
@Test
public void testBasicQuery() {
MobileElement searchBox = driver.findElement(By.id("com.example.shop:id/search_src_text"));
searchBox.sendKeys("iPhone");
driver.findElement(By.id("com.example.shop:id/search_button")).click();
// Wait for results
List<MobileElement> results = driver.findElements(By.id("com.example.shop:id/product_title"));
assert results.size() == 1;
assert results.get(0).getText().equals("Apple iPhone 12");
}
@After
public void tearDown() {
if (driver != null) driver.quit();
}
}
#### Playwright (Web) – TypeScript
import { test, expect } from '@playwright/test';
test.describe('Search functionality', () => {
test('returns correct product for query', async ({ page }) => {
await page.goto('https://shop.example.com');
await page.fill('input[placeholder="Search products…"]', 'iPhone');
await page.press('input[placeholder="Search products…"]', 'Enter');
const results = page.locator('.product-card');
await expect(results).toHaveCount(1);
await expect(results.first()).toContainText('Apple iPhone 12');
await expect(results.first()).toContainText('$49.99');
});
test('shows validation error on empty input', async ({ page }) => {
await page.goto('https://shop.example.com');
await page.press('input[placeholder="Search products…"]', 'Enter');
const error = page.locator('.search-error');
await expect(error).toBeVisible();
await expect(error).toHaveText('Please enter a search term');
});
});
These snippets illustrate how you can keep the same logical steps (fill, submit, assert) across platforms while leveraging each framework’s strengths.
Maintaining Test Suites
- Version control – store test scripts alongside application code.
- Tagging – use
@smoke,@regression,@securitylabels to enable selective runs. - Flakiness detection – integrate a retry analyzer (e.g., TestNG’s
IRetryAnalyzer) and investigate root causes rather than simply increasing retry count. - Reporting – publish HTML reports (Allure, Extent) to CI dashboards so developers can see failing search tests instantly.
Real‑World Production Edge Cases That Slip Through
Even with exhaustive test matrices, certain issues only manifest under real‑world traffic patterns or after configuration changes. Below are several categories that have historically escaped pre‑release testing.
Faceted Navigation Drift
When product attributes are updated (e.g., a new color facet is added), the search UI may still show stale facet counts or allow selection of defunct options. This leads to zero‑result pages despite valid queries. Detect by:
- Running a query that should return results, then toggling each facet and verifying that the result set updates correctly.
- Comparing facet counts against a direct aggregation query on the backend.
Personalization and A/B Tests
If your search ranking incorporates a personalization service that buckets users into variants, a test account may fall into a variant that uses a different ranking model, causing expected result order to differ. Mitigation strategies:
- Disable personalization for test users via a feature flag or by setting a known cookie/header.
- Alternatively, capture the variant ID in test logs and assert that the observed ranking matches the variant’s documented rules.
Locale‑Specific Tokenization
Search analyzers often differ by locale (e.g., Japanese uses MeCab, German uses stemming rules). A test suite that runs only with en_US may miss tokenization bugs for ja_JP or de_DE. To catch these:
- Parameterize your test data with locale identifiers.
- For each locale, run a set of queries containing language‑specific characters and verify that the tokenized output matches expectations (you can expose the analyzer via a debug endpoint).
Index Replication Lag
In distributed search clusters, writes may take seconds to propagate to all replicas. A test that immediately queries after a data‑load step can see stale results. Solutions:
- Implement a retry loop that polls for the expected document count with a timeout (e.g., 10 seconds).
- Log the replication lag metric from the cluster and fail the test if it exceeds a threshold.
Query‑Time Boosting Experiments
Product teams often run short‑lived boost experiments (e.g., “increase score of items on sale”). If the experiment is active during a test run, the result order may shift unexpectedly. Countermeasures:
- Tag test runs with a header or query parameter that disables experiments (
?exp_optout=true). - Store the experiment state in test fixtures and assert that the observed boost matches the configured value.
Checklist for Writing High‑Signal Search Test Cases
Use this checklist before you mark a test case as ready for execution. Each item can be copied into a markdown task list.
- [ ] ID and Title are unique and descriptive.
- [ ] Preconditions explicitly state index state, user authentication, and any feature flags.
- [ ] Test Data includes the exact string, file, or payload to be used.
- [ ] Steps are atomic, numbered, and avoid conjunctions like “and”.
- [ ] Expected Result specifies both what should appear and what should not appear.
- [ ] Postconditions clean up any temporary data or reset toggles.
- [ ] Priority / Severity is assigned using the risk matrix.
- [ ] Tags reflect test type (
positive,negative,performance,security,accessibility). - [ ] Traceability links to at least one requirement or user story ID.
- [ ] Automation Feasibility noted (manual, automated, or both).
- [ ] Environment Notes mention any required mocks, feature flags, or locale settings.
- [ ] Reviewed by a peer for clarity and completeness.
How to Write Test Cases for Search Functionality (With Examples): Closing Takeaways
Writing effective test cases for search is not a one‑time activity; it is a continuous loop of design, execution, analysis, and refinement. By starting with a solid anatomy, covering positive, negative, and edge‑case scenarios, anchoring each case to requirements, and balancing manual exploration with automated regression, you achieve coverage that catches both obvious defects and the subtle, production‑only issues that erode trust. Remember to:
- Treat the search index as a test artifact you control—reset and verify it before every run.
- Use data‑driven approaches to vary language, special characters, and length without duplicating test logic.
- Leverage autonomous agents like SUSA to surface unexpected interaction patterns that scripted tests might miss.
- Prioritize based on risk, and maintain traceability so you can prove coverage to stakeholders and auditors.
- Revisit the checklist whenever you add a new facet, ranking model, or localization; the search surface evolves, and your test suite must evolve with it.
Apply these practices, and you will transform search from a frequent source of user frustration into a reliable, high‑performing gateway to your application’s content. Happy testing!
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