How to Create Effective Test Cases (With Examples)

Sorry, there were no results found for “”
Sorry, there were no results found for “”
Sorry, there were no results found for “”

Most test cases fail before they catch a single bug. They’re written as vague checklists, missing preconditions, bundling multiple actions into one step, or describing expected results so loosely that two testers reading the same case would disagree on what “pass” means. The result: bugs slip through, test runs can’t be reproduced, and QA becomes a bottleneck instead of a safety net.
Writing a good test case is less about testing skill and more about verification design. Financial services call it the maker-checker process. Nuclear command calls it the two-person rule. The principle is the same: critical work should never rely on a single unchecked action. A well-written test case builds that same rigor into software. It separates what you expect from what you observe, so the gap between the two becomes impossible to ignore.
We’ll show you how to write test cases, why they matter, and how to improve test case quality over time.
A test case defines the exact steps, inputs, and expected outcomes needed to verify that a feature works correctly. Each one needs a unique ID, preconditions, and expected results written so the outcome can be checked. This guide covers the seven-step writing process, three worked examples, and how to keep a suite reliable as the product changes.
A test case is a structured document that defines the exact steps, inputs, preconditions, and expected results needed to verify whether a specific piece of software behaves correctly. It’s not a test plan (which outlines the testing strategy) or a test script (an automated code that executes the steps programmatically). A test case is the specification that both of those are built from.
Example: You’re testing the login functionality of a web application. A test case for this feature would define the following elements:
AI is now part of most testing workflows. 76.8% of testing professionals use AI in QA, with test case creation (69.6%) and script maintenance (59.6%) as the two most common uses, according to PractiTest’s 2026 State of Testing Report. Automated test cases are where that shift shows up most, so it’s worth knowing how they differ from manual ones.
| Parameter | Manual test cases | Automated test cases |
|---|---|---|
| Execution | Performed by a human tester who follows a set of documented steps | Executed by software tools, scripts, or AI agents |
| Speed | Slow and time-consuming, as humans must manually input data and verify results | Can execute hundreds of test cases simultaneously |
| Repeatability | Prone to human error and inconsistent interpretation of steps | Highly repeatable and consistent when test scripts are well-maintained |
| CI/CD integration | Difficult to integrate into fast-moving delivery pipelines due to human bottlenecks | Integrates directly into CI/CD pipelines to run tests on every build |
| Maintenance | Requires manual updates to documents whenever requirements change | Requires technical maintenance to update scripts when the UI or logic changes |
| Ideal for | Exploratory tests | Repetitive and regression tests |
You catch regressions before users file tickets. Every code change carries the risk of breaking something that already works. A well-written test case becomes a standing checkpoint that runs after every deployment. When a developer refactors your login code a year on and accidentally breaks session handling, that test case is what flags it in staging.
You make pass/fail a fact, not an opinion. Vague expected results like “the system responds appropriately” force every tester to interpret what “appropriate” means. Two testers run the same case, one marks it pass, the other flags a defect, and now the team is debugging the disagreement instead of the software. When your expected result reads “system displays error message: ‘Invalid password’ and keeps the user on the login page,” there’s no room for interpretation. The result either matches or it doesn’t. That’s the two-person rule in practice: the test case is the maker, the tester is the checker, and they both need to speak the same language.
You turn tacit knowledge into a reusable asset. In most teams, the senior QA engineer carries an invisible map of every edge case, every workaround, every “oh, make sure you also check X.” When that person goes on leave or changes teams, the map goes with them. Documented test cases with explicit preconditions and boundary values preserve that knowledge structurally. A new tester joining the team can pick up TC_LOGIN_005 and test the account-lockout-after-five-attempts flow on day one, without asking anyone what the threshold is or how the timer resets.
You isolate failures to exact steps, not general areas. When a test case bundles “navigate to the page, enter credentials, and click submit” into a single step and the test fails, all you know is “something in the login flow broke.” When each action is its own step with its own expected result, the failure lands on step 4: “Click ‘Send Reset Link’ → expected success message, got 500 error.” That precision cuts debugging time dramatically because the developer knows exactly which interaction triggered the defect, not just which feature area to search.
You see what’s covered and what’s a blind spot. Without structured test cases, test coverage is a guess. With them, you can map every case back to a requirement and instantly spot gaps. If your password reset feature has six scenarios (successful reset, expired link, reused link, unregistered email, invalid format, multiple requests) and you only have test cases for three, the gap is visible and quantifiable. That visibility is what turns testing from “we tested it” into “here’s exactly what we tested, here’s what we didn’t, and here’s the risk we’re accepting.”
A useful test case does more than describe what to test. It captures the context, execution steps, and expected system behavior so that another tester, developer, or product manager can reproduce the test and verify the outcome.
The components of a test case are:
For the website login functionality example we shared above, your test case should include:
Test case ID: Every test case needs a unique identifier. When testing a feature, QA teams often create multiple test cases that validate similar conditions. The test case ID helps track, organize, and reference them easily during debugging or reporting.
Example: TC_LOGIN_001
Description: This explains what functionality the test case is validating. It provides a short summary so anyone reading the test case immediately understands its purpose.
Example: Verify that a registered user can successfully log into the application using valid credentials.
Preconditions: Preconditions describe the system state required before executing the test case. Without them, testers may run the same test under different conditions and get inconsistent results.
Examples:
Steps: These are the actions a user or tester performs to execute the test case. Each step should be clear and sequential so that anyone on the team can reproduce the test.
Expected results: This defines what the system should do if the feature is working correctly.
If the credentials are invalid, the system should display the appropriate error message.
Actual results: These capture the tester’s observations after running the test case. If the observed behavior differs from the expected result, the issue is logged as a defect.
Example observation:
Now let’s put your test case writing skills to use.
Did You Know? Only 2.1% of teams describe their AI testing practices as optimized, while over 85% are still in the initial or experimenting stages. The most common use is generating test cases (69.6%), not strategic work like risk identification (19.9%).
Writing a test case takes seven steps: analyze the requirement, list scenarios, plan structure, write steps with expected results, attach context, get it reviewed, then execute and log.
Before you write the test case, understand what the feature is supposed to do. This is where you review available documents—PRDs (product requirement documents), user stories, feature specifications, and design docs—and identify every piece of functionality that needs to be verified.
Example: You’re building a feature that allows users to reset their passwords via email. To build a test case around this, you need to understand:
This clarity gives you the foundation to frame one clear objective for your test case.
Objective: Verify that a registered user can successfully reset their password via email.
Next, list out the test scenarios you need to validate. A test scenario is a high-level situation—one that will typically branch into multiple test cases covering different inputs and outcomes.
For the password reset feature, your scenarios might look like:
Each scenario here will translate into one or more test cases covering specific inputs and conditions. Breaking the feature down this way ensures you have test coverage across both expected behavior and the edge cases real users will inevitably hit.
Read More: How to Create and Implement a QA Checklist
For repetitive regression testing, you need a structure that lets you document your test case and its results consistently. A well-defined test case template brings that consistency and allows reusability without starting from scratch every time.
Plan your test execution by clarifying these elements:
What role or qualification does the person running this test need? Depending on the complexity of the test and required human intervention, assign roles:
List every condition that must be true before step 1, and make each one checkable by the tester:
Example:
Define the exact input values needed to run the test—valid data, invalid data, and boundary values.
Example:
Break down the execution process into sequential steps. Use consistent terminology and ensure each step has one action. When defining steps, also outline the expected result and what constitutes a pass or fail.
Continuing our password reset flow, here’s what testing steps would look like:
| Steps | Expected result |
| Navigate to the login page | The login page loads with a clickable “Forgot Password” link |
| Click “Forgot Password” | User is redirected to the password reset request page |
| Enter Email in the Email field | Email is accepted with no validation error |
| Click “Send Reset Link” | Success message displays: “Reset link sent to test@example.com” |
| Open the reset link from the email | User is redirected to the new password creation page |
| Enter a valid new password | Password field accepts input with no error |
| Click “Reset Password” | Success message displays, and the user is redirected to the login page |
Now define steps and expected results for alternative scenarios (discussed earlier). Say what happens when the user enters an invalid email or the password doesn’t match the pre-determined criteria.
Bonus: Here’s how you can automate documentation using AI for all your test cases.
Include relevant documents or attachments that will help testers execute the test case with full context and no ambiguity. This can include:
Share the written test case with a peer or senior QA lead before executing it. During the review, check that:
Conduct the test and log the actual result against each expected result. Mark a test as pass or fail at every step. For every failed step, raise a bug immediately and link it back to the test case. Further, if any unexpected behavior occurs that isn’t a clear pass or fail, note it in the comments field for further review.
Read More: How to Use AI for Quality Assurance
These three examples show how the same test case structure adapts to different types of software testing. Each one uses the components and step format from earlier in this guide, but the complexity, test data, and failure modes shift depending on what you’re verifying.
A QA team at an online retailer is testing the checkout experience before a holiday sale. The flow spans multiple pages: cart → shipping → payment → confirmation. The distinguishing challenge here is state dependency: each step relies on the previous step completing correctly, and the test data (cart contents, shipping address, payment method) must persist across all of them.
Tester: Rahul D.
Test date: 09/03/2026
Test case ID: TC_CHECKOUT_003
Description: Verify that a logged-in user can complete a purchase using a saved credit card and standard shipping.
Preconditions:
| Step | Expected result | Actual result | Pass/Fail |
| Navigate to the cart page | Cart displays the correct item, quantity, and subtotal | As expected | Pass |
| Click “Proceed to Checkout” | Shipping page loads with the saved address pre-selected | As expected | Pass |
| Select “Standard Shipping” and click Continue | Payment page loads showing the order total with shipping cost added | As expected | Pass |
| Confirm the saved credit card and click “Place Order” | Order confirmation page displays with order number, item summary, and estimated delivery date | Payment page reloads with error: “Unable to process payment” | Fail |
Result summary: The checkout flow handles cart-to-shipping correctly, but payment processing fails on saved credit cards. Defect logged: the tokenized card lookup times out when the payment gateway takes longer than 3 seconds to respond.
A backend engineer is testing the “Create User” API endpoint before it’s consumed by the front-end team. There’s no interface to click through: the test case validates request payloads, response codes, and data persistence directly. The distinguishing challenge is testing the contract between systems, not the user experience.
Tester: Sarah S.
Test date: 09/05/2026
Test case ID: TC_API_USER_001
Description: Verify that a POST request to /api/v1/users creates a new user and returns the correct response.
Preconditions:
| Step | Expected result | Actual result | Pass/Fail |
Send POST to /api/v1/users with valid payload: { "name": "Test User", "email": "newuser@testdomain.com", "role": "viewer" } | Response returns 201 Created with a JSON body containing user ID, name, email, and role | 201 returned with correct body | Pass |
| Send the same POST request again with the identical email | Response returns 409 Conflict with message: “User with this email already exists” | 200 OK returned; duplicate user created | Fail |
| Send POST with missing “email” field | Response returns 400 Bad Request with validation error: “Email is required” | 400 returned as expected | Pass |
| Query GET /api/v1/users/{id} using the ID from step 1 | Response returns 200 OK with the user details matching the original payload | As expected | Pass |
Result summary: The endpoint creates users correctly and validates required fields, but fails to enforce email uniqueness at the database level. Duplicate records were created without error. Defect logged with severity: High.
A security team is testing whether the application correctly restricts actions based on user roles before a compliance audit. The distinguishing challenge is that you’re not testing whether a feature works; you’re testing whether a feature is properly denied. The expected result for most steps is a block, not a success.
Tester: Marcus L.
Test date: 09/08/2026
Test case ID: TC_RBAC_002
Description: Verify that a user with “Viewer” role cannot create, edit, or delete projects.
Preconditions:
| Step | Expected result | Actual result | Pass/Fail |
| Navigate to the Projects page | Viewer sees the project list in read-only mode; “Create Project” button is either hidden or disabled | Button is visible but greyed out | Pass |
| Attempt to click “Create Project” | System prevents action; no new project form loads | No form loaded; tooltip shows “You don’t have permission” | Pass |
| Open an existing project and attempt to edit the title | Title field is non-editable, or system blocks the save | Title field was editable; changes saved successfully | Fail |
| Attempt to delete the project via the three-dot menu | Delete option is hidden, or action is blocked with a permission error | Delete option not visible in menu | Pass |
Result summary: Create and delete permissions are correctly restricted for Viewers, but edit permissions are not enforced at the field level. A Viewer can modify project titles despite having read-only access. Defect logged with severity: Critical (compliance blocker).
Test cases can be managed in a dedicated QA tool (TestRail, Zephyr), a general PM tool (ClickUp, Jira), or a spreadsheet; the right pick depends on whether you need built-in execution or just tracking.

ClickUp for Software Teams is a project management platform where test cases live as tasks alongside the sprints, bugs, and pull requests they relate to. It’s not a dedicated test management tool, but its flexible task structure lets teams build test case workflows using custom statuses, fields, and task types without needing a separate tool.
Here’s what a G2 reviewer has to say:
What I like best about ClickUp is that it brings everything into one place. Tasks, timelines, notes, and updates all live in the same system, which reduces the back and forth between tools.
I also value the flexibility. We can customize statuses, fields, and views to match how our team actually works. That makes it easier to stay organized and gives clear visibility into who is responsible for what and where projects stand at any given time.
Best for: A QA lead who wants a failed test to become a developer’s bug ticket in one click, with the PR, the test steps, and the sprint all visible from the same task.
Skip it if: Your test cycle is mostly automated. If 80% of your suite runs from a CI pipeline, you need a tool that ingests execution results, not one where a human updates a status.

TestRail is a dedicated test management platform built for QA teams that need structured control over their entire testing process. It handles the full cycle of writing, running, and reporting on test cases, with DevOps and CI/CD integrations feeding results in.
Here’s what a G2 reviewer has to say:
What I find most valuable about TestRail is that it provides our QA team with a secure and well-organized environment to manage test plans, test cases, and test runs. I appreciate how straightforward it is to structure and implement test suites, as well as to reuse previously created test cases. The integrations with Jira and CI/CD tools have made our workflow more cohesive and easier to track. Having the ability to monitor test progress and coverage in real time has been incredibly helpful for sprint planning and reporting. In my daily work as a QA Engineer, using TestRail has also saved me a significant amount of time.
Best for: A QA team of five or more running formal test cycles per release, where a manager needs to answer “what percentage of Release 2.0 has been executed and passed” from a dashboard, not a spreadsheet.
Skip it if: Your suite is under a few hundred cases or your testers double as developers. At that scale, the per-seat cost and the separate login buy you reporting you won’t look at.

Zephyr is SmartBear’s test management plugin for Jira, built to handle test cases from inside the Jira interface. It supports both manual and automated testing, with strong reporting and traceability features for agile and enterprise teams.
Here’s what a G2 reviewer has to say:
This is a best tool to import testcases directly from excel sheet. Using this tool testers work is getting less than importing testcases in jira. Also best feature for this tool to make test cases pass or fail and adding attachment.
Best for: Regulated or audit-heavy teams (fintech, healthcare) that need every test traced to a Jira requirement and every defect linked back to the test that found it, all inside one Atlassian instance.
Skip it if: Your Jira instance is large and your QA team is small. A 200-seat Jira with 10 testers pays for 190 Zephyr licenses nobody opens; a standalone tool priced per tester costs less.

Jira is Atlassian’s project management and issue tracking platform, widely used by engineering and QA teams to manage sprints, bugs, and development workflows. While it isn’t a dedicated test management tool, many teams use it alongside solutions like Zephyr Scale or Xray to handle test case management within their existing Jira setup.
Here’s what a G2 reviewer has to say:
Jira is one of my favorite digital programs for tracking and visualizing the performance of all my work projects in collaboration with all the members of my work team because it offers the best virtual performance capabilities in its class, making it easier to achieve all my professional goals.
Best for: Engineering teams evaluating whether they need dedicated test management at all. Running a few test cycles as custom Jira issue types first shows whether the volume justifies a plugin.
Skip it if: You already know you need test management. Going straight to a plugin or standalone tool avoids the migration when custom issue types stop scaling.
Avoid these test case writing mistakes that can reduce its overall effectiveness.
| Mistake | What to do instead |
|---|---|
| Writing test cases too late | Involve testers in requirements gathering and design phases to identify potential issues before coding begins |
| Not updating test cases | Update the test case as soon as the feature gets a new upgrade, a change in functionality, or a UI change |
| No postconditions | State what the system should look like after the test completes (test user deleted, cart emptied, session closed) so the next case starts from a clean state |
| Only happy-path data | Every input field needs at least one value that the system should reject. Document how the dataset is generated or pulled |
| Not prioritizing test cases | Assign each test case a priority based on business impact, frequency of use, and risk of failure. This helps you focus testing efforts and make smarter calls on budget and testing methods |
A test suite stays trustworthy when each case is independent, targets the inputs most likely to break, and gets retired the moment it stops producing a reliable verdict. These six practices separate suites that catch bugs for years from suites that get ignored after the second sprint.
A test case should set up its own state and never depend on another case having run first. When TC_CHECKOUT_003 assumes TC_CHECKOUT_002 left an item in the cart, one failure cascades into five, and you spend the morning figuring out which failure was real. If a test title needs the word “and,” split it into two cases.
Bugs cluster at the edges of accepted input, not the middle. If a password field accepts 8 to 128 characters, test 7, 8, 128, and 129, not just a comfortable 12-character password. Boundary value analysis is a formal technique in the ISO/IEC/IEEE 29119-4 test design standard for exactly this reason: it finds off-by-one errors that random inputs miss.
Group inputs that the system should treat identically, then test one representative from each group. Every valid email format behaves the same way in a login form, so one valid email is enough. Testing user@example.com, jane@company.com, and bob@domain.org as three separate cases triples your maintenance load without adding coverage.
Hardcoding “enter test@example.com” into a step means rewriting the step every time the test environment changes. Keep steps generic (“enter a registered email”) and hold the actual values in a test data field or file. The same steps then run against staging, QA, and pre-production without edits, and swapping in a new dataset for a negative test is a one-line change.
A flaky test fails inconsistently without a real product bug, and every flaky test teaches the team to ignore red results. Track how often each case flips between pass and fail across identical builds. If a case flakes more than a couple of times a month, rewrite or remove it. Pair that with defect escape rate (bugs found in production that a test case should have caught) to see where coverage is thin, not just where it’s noisy.
Regression, smoke, and high-frequency functional cases are the strongest automation candidates because they run on every build and rarely change. Move those to scripts in your CI/CD pipeline, and use AI-assisted tools with self-healing locators where UI elements shift often. That frees human testers for exploratory work and the types of software testing that need judgment, like usability and edge-case hunting.
The tools section above covers where ClickUp fits. This section shows the actual setup, mapped to the steps from earlier in the guide.
Structure your test case library. Create a Space for your product, a Folder for each feature area (e.g., Authentication, Payments, Onboarding), and a List for each test cycle or sprint. Each task becomes an individual test case. Use ClickUp Custom Fields to capture components like test case ID, preconditions, test data, priority level, and environment.
Write test steps directly in the task. Use the task description to document sequential execution steps and expected results. Checklists work well for step-by-step flows where a tester needs to mark off each action, while the description holds context like preconditions and test data.
Track execution and results. Build Custom Statuses that mirror your test workflow: Not Started → In Progress → Pass → Fail → Blocked. When a test fails, convert it to a bug task or create a linked task assigned to the developer, with priority, dependencies, and a deadline. The GitHub and GitLab integrations let you link that bug directly to the PR that introduced it. If the root cause is a code defect, you can assign that bug task to the ClickUp Codegen Agent, which reads the task description, linked specs, and comments, writes a proposed fix, and opens a pull request for review with progress posted back to the task.
Pro Tip: QA leads can get an instant snapshot of any task by asking ClickUp Brain to summarize open bug tasks. This way, they have all the context they need for sprint reviews without digging through individual tasks to piece together the picture.

Run test cycles with views. Use Board View grouped by status to see pass/fail distribution at a glance during a test run. Table View works as a traditional test matrix when you need to scan results across dozens of cases. Filter by assignee to balance workload, or by priority to focus a smoke test on the critical paths first.
The ClickUp Test Management Template gives you a centralized way to manage an entire testing workflow across multiple feature areas, test scenarios, and edge cases in one place. Use it to track user feedback, manage test schedules, monitor the progress of your tests, and evaluate pass/fail results without jumping between tools.
A test case earns its place the moment two people can run it independently and land on the same pass or fail. Everything in this guide (one action per step, explicit preconditions, expected results with no room for interpretation) serves that single standard. If you already plan sprints in ClickUp, the setup covered above lets you write, run, and track test cases alongside the bugs they surface, without adding another tool.
A single requirement can need one test case or ten, depending on how many scenarios, edge cases, and input variations it involves. The idea is to cover all realistic paths, offering sufficient coverage without redundancy.
A test case documents what to test and what result to expect, written for manual execution. A test script, on the other hand, is the automated version: code that executes those same steps programmatically.
Break the test into logical, sequential steps that are easy to follow without any prior context. Don’t bundle multiple actions together or add unnecessary complexity.
A test scenario states what to test in one line (“Verify password reset”); a test case specifies how, with preconditions, steps, test data, and expected results. One scenario typically produces 3 to 10 test cases covering the happy path, invalid inputs, and boundary conditions. Scenarios come first and drive coverage planning; test cases come second and drive execution.
A positive test case uses valid input and expects success: correct email and password log the user in. A negative test case uses invalid or unexpected input and expects the system to fail gracefully: a wrong password shows “Invalid password” without creating a session. Mature test suites skew roughly 1:3 to 1:5 positive to negative, because most production bugs live in error paths, not happy paths.
A test plan defines the scope, approach, resources, and schedule for an entire testing effort. A test suite is a collection of test cases grouped for a single run, such as “regression suite for release 2.1.” A test case is the atomic unit inside both: one documented check with one pass/fail outcome. Plan sets strategy, suite sets scope, case sets the verdict.

Praburam Srinivasan
Max 25min read

Engineering Team
Max 12min read

Praburam Srinivasan
Max 24min read

© 2026 ClickUp