QA How-To
Writing test cases for a search feature (2026)
Learn writing test cases for a search feature, covering relevance, filters, Unicode, indexing, accessibility, performance, analytics, and QA automation.
22 min read | 2,963 words
TL;DR
For writing test cases for a search feature, define invariants, rank risks, partition inputs, test boundaries and state transitions, inject dependency failures, and verify both the user result and durable state. Automate each check at the lowest reliable layer.
Key Takeaways
- Define the search feature contract and observable invariants before writing steps.
- Prioritize relevant records not appearing after indexing and other high-impact failures.
- Use partitions, boundaries, and state transitions to reduce blind spots.
- Assert durable side effects as well as the visible response.
- Test authorization across users, roles, tenants, actions, and states.
- Automate rules at service level and keep a thin critical browser suite.
Writing test cases for a search feature starts with the business risk, not a list of UI clicks. Writing test cases for a search feature means proving that each query produces relevant, permitted, stable results that match the documented search semantics, including when dependencies are slow, data is hostile, or a retry arrives at the worst possible moment.
This guide gives working QA engineers a repeatable method for turning requirements into precise cases, choosing test data, automating the right layers, and explaining the strategy in an interview. Examples are deliberately risk-based, so the suite can find consequential defects instead of merely accumulating case counts.
TL;DR
Start with invariants and a state model. Partition inputs, test every boundary, cover authorization and failure recovery, then automate stable checks at the lowest useful layer. A strong suite for a search feature correlates the user result with service state and observable evidence.
| Dimension | Controlled corpus example | Expected check |
|---|---|---|
| Recall | Exact and synonym documents | Both intended documents appear |
| Precision | Ambiguous term plus irrelevant document | Irrelevant item stays below threshold |
| Ranking | Title match versus body-only match | Documented boost wins |
| Freshness | Old and newly updated equal documents | Freshness rule is applied |
| Security | Public and restricted documents | Restricted item never appears |
1. Define the search feature contract before writing cases
A test case is only as good as its oracle. Write down who performs the operation, required preconditions, accepted inputs, state changes, external calls, visible result, and audit evidence. For this feature the central promise is relevant, permitted, stable results that match the documented search semantics. Turn that promise into assertions that can be checked independently.
Separate product policy from implementation behavior. Requirements should answer limits, ownership, error semantics, retry rules, time behavior, localization, and cleanup. If a decision is genuinely unspecified, record it as a question or risk. Do not silently encode whichever behavior the current build happens to show.
Use a compact case format: ID, risk, preconditions, data, action, expected response, expected durable state, and evidence. Add priority and automation layer only after the behavior is clear. This produces cases a developer can reproduce and an interviewer can evaluate. The same discipline is explained in the ecommerce cart test cases.
2. writing test cases for a search feature: build a risk model
List failures in business language before listing test steps. The highest-value risks here include:
- relevant records not appearing after indexing
- restricted records leaking in results or suggestions
- unstable ranking across repeated requests
- filters changing counts incorrectly
- Unicode normalization producing false misses
- analytics changing user-visible behavior
Score each risk qualitatively by impact, likelihood, detectability, and recovery difficulty. A rare defect can remain critical when it exposes data or creates irreversible state. Map every critical risk to at least one positive case, one negative case, and one recovery case.
A useful traceability row is: risk -> control -> test -> observable signal. For relevant records not appearing after indexing, identify the prevention mechanism and the evidence that proves it worked. For restricted records leaking in results or suggestions, assert both what the searcher sees and what downstream systems store. This avoids the common mistake of treating a successful screen or HTTP response as the only truth. Review the model when architecture, policy, or dependencies change.
3. Partition inputs instead of guessing examples
Equivalence partitioning reduces repetition without weakening coverage. Build dimensions from the contract, then select pairs that expose interactions. Important partitions for this feature are:
- Exact term, prefix, phrase, typo, synonym, and no-match query.
- Anonymous and entitled visibility.
- Single and combined filters.
- Relevance, price, date, and user-selected sort.
- Latin, accented, CJK, emoji, and right-to-left input.
- Fresh, updated, and deleted indexed document.
Do not create the full Cartesian product. Pairwise selection can cover ordinary interactions, while explicit risk cases cover combinations such as exact term, prefix, phrase, typo, synonym, and no-match query with relevance, price, date, and user-selected sort, or single and combined filters with fresh, updated, and deleted indexed document. Keep one baseline case with the smallest valid data set, then change one dimension at a time when diagnosis matters.
For each partition, define why values should behave alike and what would falsify that assumption. Add malformed, missing, duplicated, and unexpected values at service boundaries. Client controls are useful feedback, but server enforcement remains the authoritative check. Record normalized values when normalization is part of the contract.
4. Model lifecycle states and forbidden transitions
State-transition testing finds defects that happy paths miss. A practical model for this feature includes: not_indexed, indexing, searchable, updated_pending, deleted_pending, removed. Draw permitted transitions and name the event that causes each one. Then test valid transitions, repeated events, late events, and attempts to jump directly to a later state.
Every transition case should assert the previous state, triggering command or event, next state, durable side effects, and emitted notification. Repeating an operation should have a documented result: idempotent success, explicit conflict, or a new operation. Silence is not a specification.
Pay special attention to terminal and recovery states. Test retry after a transient failure, retry after an ambiguous result, cancellation during work, expiry, and cleanup. For unstable ranking across repeated requests, send duplicated and reordered signals when architecture allows it. Assert that state moves only forward under the domain rules and that an operator can reconcile ambiguous outcomes from identifiers and timestamps.
5. Apply boundary value analysis
Boundary tests should name the rule they challenge. Cover these edges:
- empty, whitespace-only, minimum, maximum, and over-limit query
- zero, one, page-size, and page-size-plus-one matches
- one filter, maximum filter count, and unsupported filter
- special characters at the start, middle, and end
- same score ties at page boundaries
For each numeric limit, test below, at, and above it with exact units. For strings, count according to the contract, which may mean bytes, Unicode code points, or user-perceived characters. For time, pin the clock or provide an explicit timestamp so tests do not fail around midnight.
Combine only boundaries with a plausible interaction. For example, pair empty, whitespace-only, minimum, maximum, and over-limit query with a retry or permission change if the architecture treats it differently. Assert not just rejection, but that no partial side effect remains. Clear messages should identify the invalid field or rule without revealing internal details. Boundary cases belong heavily at the API or service layer because they are faster and more deterministic there.
6. Design test data that explains failures
Good data makes the expected result obvious. Use small hand-curated relevance corpus; documents differing by one controlled attribute; multilingual and normalized text pairs; restricted documents owned by separate users; fixed popularity and freshness signals. A named builder should create a valid baseline and allow a test to override only relevant fields. Avoid a giant shared fixture whose history no one understands.
Keep identities separate for ownership and authorization checks. Generate unique business identifiers for parallel runs, but fix values involved in calculations or ordering. Store expected outcomes alongside the seed definition, not copied from the application response. If production-like samples are required, synthesize or irreversibly sanitize them according to policy.
Create a small golden data set for deterministic functional checks and separate high-volume data for performance or exploratory work. Cleanup must be safe under partial failure. Prefer API-supported deletion, test namespaces, expiry policies, or run IDs over broad database deletion. A rerun should succeed even after the previous run stopped halfway.
7. Cover integrations and failure recovery
The feature crosses search box and results UI, query service, search index, catalog or source database, permissions service, analytics pipeline. For each boundary document the request, response, timeout, retry owner, idempotency rule, authentication, and source of truth. Then test success plus timeout, unavailable dependency, malformed response, slow response, duplicate callback, and recovery.
Use real sandbox integrations for a small confidence suite and controlled fakes for deterministic fault injection. A mock proves how your code reacts to the response you configured, not that the provider behaves that way. Contract tests and scheduled sandbox checks close that gap.
When injecting filters changing counts incorrectly, verify three outcomes: the searcher receives an accurate status, durable state remains reconcilable, and retry does not duplicate the operation. Capture identifiers across boundaries. If the workflow is asynchronous, poll a documented status with a bounded deadline and useful diagnostics. Fixed sleeps make suites slower and hide the actual completion condition.
8. Test authorization, privacy, and abuse resistance
Treat authorization as a matrix of subject, resource, action, and state. Test the owner or permitted role, an unrelated identity at the same privilege level, a lower role, a higher role where relevant, another tenant, and an anonymous client. Repeat checks for read, create, update, retry, cancel, and delete because endpoints often enforce them inconsistently.
Validate content and identifiers on the server. Try guessed IDs, stale tokens, replayed requests, unsupported fields, and values that resemble markup, control characters, or paths. Assertions should focus on safe rejection and absence of side effects, not on exploiting a live system. Use only authorized test environments and approved security artifacts.
For analytics changing user-visible behavior, inspect response bodies, browser storage, analytics, application logs, traces, and support-visible errors. Sensitive values should be redacted while correlation remains possible. The accessibility testing checklist provides a complementary view of layered checks.
9. Validate usability and accessibility
Functional correctness does not excuse an unusable workflow. Test the primary task with keyboard only, visible focus, programmatic names, logical reading order, zoom, narrow viewport, and assistive-technology-friendly status updates. Errors should identify the problem, preserve safe user input, and explain recovery without relying only on color.
Verify loading, empty, success, partial, and failure states. Prevent accidental repeated actions while preserving deliberate retry. If work continues asynchronously, communicate current state and let users return later when the product supports it. Localized text must not break layout, and formatted values should follow the selected locale without changing underlying meaning.
Accessibility selectors also improve automation. Prefer role, label, and visible name when they represent user behavior. Use test IDs for stable non-semantic containers or values that have no suitable accessible locator. Do not make automated accessibility scans the entire strategy, since task flow, focus, and message quality still need human evaluation.
10. Automate at the right layer
Place validation rules, transitions, and combinatorial cases at unit or service level. Put contract, authorization, persistence, and integration behavior at API level. Reserve browser tests for a few critical journeys, accessible interaction, and wiring. This test pyramid gives fast diagnosis without ignoring the user experience.
The following Playwright test uses supported Playwright Test APIs. Its example endpoints, labels, and fixture values must be aligned with the application contract:
import { test, expect } from '@playwright/test';
test('filters persist and restricted results stay hidden', async ({ page }) => {
await page.goto('/search');
await page.getByRole('searchbox', { name: 'Search' }).fill('wireless keyboard');
await page.getByRole('checkbox', { name: 'In stock' }).check();
await expect(page).toHaveURL(/q=wireless%20keyboard/);
await expect(page).toHaveURL(/inStock=true/);
const cards = page.getByTestId('search-result');
await expect(cards.first()).toBeVisible();
await expect(page.getByText('Internal prototype')).toHaveCount(0);
await page.reload();
await expect(page.getByRole('checkbox', { name: 'In stock' })).toBeChecked();
});
Keep each test independent and assert the business outcome, not arbitrary implementation timing. Generate unique records, wait on observable conditions, and attach request IDs or traces on failure. Run the critical risk set on every change, broader compatibility suites on a schedule, and sandbox integration checks at a frequency that respects provider limits. See Playwright locator best practices for related automation practice.
11. Add nonfunctional and operational coverage
Performance tests need a workload model, not a single response-time assertion. Define realistic request mixes, data sizes, concurrency, ramp pattern, cache state, and success criteria. Measure end-to-end latency and dependency time separately. Include a short burst, sustained load, and recovery after pressure when the system's risk warrants them.
Test resilience with bounded fault injection: dependency latency, transient errors, lost responses, restarts, and queue backlogs. Verify backpressure, retry limits, circuit behavior, and graceful degradation according to design. Confirm that recovery processes old work without duplication or starvation. Never run disruptive tests against shared or production systems without explicit authorization.
Operational acceptance also includes deploy and rollback behavior, schema compatibility, feature flags, audit retention, and alert usefulness. A release is not healthy merely because requests return success. Confirm that critical metrics and logs distinguish user error, dependency failure, and product defect.
12. Make failures diagnosable and maintainable
Capture normalized query, applied filters and sort, index version, result IDs and scores where permitted, permission decision, query latency and trace ID. A failed test should report its data seed, identity, operation identifiers, expected state, observed state, and relevant response without leaking secrets. Correlation across UI, API, worker, and dependency reduces investigation time dramatically.
Tag cases by risk and capability, not by a person's name. Review flaky tests as product signals first, then determine whether the defect is in synchronization, environment, test data, or application behavior. Quarantine can protect a pipeline briefly, but every quarantine needs an owner and removal condition.
Maintain traceability from requirement or risk to automated and exploratory coverage. Delete redundant tests when a lower-layer check provides the same confidence, but retain a thin end-to-end proof. During change review, ask which contract, state, boundary, integration, or observable changed. That question scales better than rereading hundreds of step-by-step scripts.
13. writing test cases for a search feature: run focused exploratory charters
Scripted cases confirm known expectations, while exploratory charters search for interactions the model did not predict. Time-box each charter, name a risk, prepare data and tools, and record observations, questions, defects, and coverage notes. A useful first charter is to interrupt the query at every visible transition while changing network conditions. Watch whether the interface tells the truth, whether retry is safe, and whether cleanup completes.
A second charter should vary identity and session context. Begin as one searcher, open the workflow in another tab or device, then sign in, sign out, expire the session, or change permissions where the test environment supports it. Inspect direct URLs, cached content, notifications, and background requests. This is especially useful for finding Unicode normalization producing false misses. Do not perform intrusive security activity outside an explicitly authorized environment.
A third charter should stress representation rather than volume. Combine long Unicode text, locale changes, clock boundaries, duplicate actions, back navigation, refresh, and an accessibility tool. Observe focus, announcements, truncation, normalization, and recovery. Capture the exact seed and correlation IDs so a discovery can become a reproducible regression case.
End each session with a short debrief: what was tested, what was not, which assumptions changed, and which new risks deserve scripted coverage. Do not convert every observation into a browser test. Add a unit, service, API, contract, or UI regression at the layer that most directly protects the violated rule. Update the risk model and state diagram when exploration reveals that the original model was incomplete.
Interview Questions and Answers
Q: How do you test search relevance?
I use a versioned, curated corpus and judged queries. I assert invariant relationships, such as an exact title match ranking above a weak body match, rather than freezing every score.
Q: What is the difference between correctness and relevance?
Correctness asks whether filters, permissions, counts, and matching rules behave as specified. Relevance asks whether the ordering best satisfies user intent, which needs judged examples and product-approved tradeoffs.
Q: How do you test eventually consistent indexing?
I trigger create, update, and delete events, then poll a documented status or search condition until a bounded deadline. I measure indexing delay separately from query latency.
Q: Which Unicode cases matter?
I cover composed and decomposed accents, case folding, locale-sensitive characters, CJK text, emoji, right-to-left text, and safe handling of control or special characters.
Q: How do you test autocomplete?
I verify minimum length, debounce, keyboard navigation, stale-response suppression, highlighting, permissions, empty states, and selection analytics.
Q: How do you prevent brittle ranking tests?
I assert stable ranking relationships and result-set properties on controlled data. Exact numeric scores are asserted only when the scoring contract exposes and guarantees them.
Q: What security issue is unique to search?
Suggestions, facets, counts, snippets, and cached pages can leak restricted content even when result cards are hidden. I test every search surface with different identities.
A credible interview answer explains the risk, test technique, oracle, and automation layer. Naming many scenarios without explaining expected state or failure evidence is weaker than a smaller, structured model.
Common Mistakes
- Testing only the successful user journey and ignoring retries, interruption, and recovery.
- Treating a UI message or HTTP status as proof that durable state is correct.
- Copying production data into lower environments without approved sanitization.
- Asserting implementation details so tightly that harmless refactoring breaks the suite.
- Using fixed sleeps instead of waiting for a documented condition with a deadline.
- Skipping horizontal authorization tests between two ordinary users or tenants.
- Combining too many variables in one case, making failures difficult to diagnose.
- Automating every case through the browser instead of choosing the lowest useful layer.
- Ignoring logs, traces, cleanup, alerts, and other operational acceptance criteria.
Use mistakes as review prompts. For every critical case, ask what evidence proves the outcome, what happens after ambiguity, and whether another identity can produce a different result.
Conclusion
Writing test cases for a search feature is a modeling exercise before it is a documentation exercise. Define the contract and invariants, rank risks, partition inputs, test boundaries and state transitions, inject integration failures, and verify authorization plus durable side effects.
Start with the six highest-impact risks in this guide. Build one clear case for normal behavior, one for rejection, and one for recovery for each risk, then automate them at the lowest reliable layer. Review those cases with product, engineering, security, and operations so the expected outcome reflects the whole system. Keep evidence from each failure concise and safe, and update the model whenever a production lesson changes an assumption. That creates a suite that supports releases, incident diagnosis, and strong SDET interview answers.
Interview Questions and Answers
How do you test search relevance?
I use a versioned, curated corpus and judged queries. I assert invariant relationships, such as an exact title match ranking above a weak body match, rather than freezing every score.
What is the difference between correctness and relevance?
Correctness asks whether filters, permissions, counts, and matching rules behave as specified. Relevance asks whether the ordering best satisfies user intent, which needs judged examples and product-approved tradeoffs.
How do you test eventually consistent indexing?
I trigger create, update, and delete events, then poll a documented status or search condition until a bounded deadline. I measure indexing delay separately from query latency.
Which Unicode cases matter?
I cover composed and decomposed accents, case folding, locale-sensitive characters, CJK text, emoji, right-to-left text, and safe handling of control or special characters.
How do you test autocomplete?
I verify minimum length, debounce, keyboard navigation, stale-response suppression, highlighting, permissions, empty states, and selection analytics.
How do you prevent brittle ranking tests?
I assert stable ranking relationships and result-set properties on controlled data. Exact numeric scores are asserted only when the scoring contract exposes and guarantees them.
What security issue is unique to search?
Suggestions, facets, counts, snippets, and cached pages can leak restricted content even when result cards are hidden. I test every search surface with different identities.
Frequently Asked Questions
How many test cases are enough for a search feature?
There is no universal count. Coverage is sufficient when material risks, contract rules, partitions, boundaries, states, identities, dependencies, and recovery paths are traceable to tests, with residual risk explicitly accepted.
What should every search feature test case include?
Include the risk, preconditions, identity, data, action, expected response, expected durable state, and diagnostic evidence. Add priority and automation layer after expected behavior is unambiguous.
Should search feature tests be automated?
Automate stable, repeatable, high-value checks. Put rules and combinations at unit or service level, contracts and authorization at API level, and retain a small browser suite for critical user journeys.
How should negative cases be selected?
Derive them from invalid partitions, boundaries, forbidden state transitions, unauthorized subjects, dependency faults, retries, and malformed inputs. Random invalid values alone do not provide a defensible strategy.
How can flaky tests be avoided?
Isolate data, control time and dependencies, wait for observable conditions, avoid execution-order dependencies, and capture correlation identifiers. Investigate flakiness instead of hiding it with unconditional retries.
What is the best way to prioritize these tests?
Prioritize by business impact, likelihood, detectability, and recovery difficulty. Run irreversible, security-sensitive, money-sensitive, and core-journey risks earliest.
How should test results be reported?
Report the expected and actual business state, inputs, identity, environment, build, timestamps, and safe correlation IDs. Attach minimal reproduction evidence without exposing sensitive data.