A test case is the fundamental unit of work for every QA engineer. It is a documented set of conditions, inputs, steps and expected results that verify whether a specific aspect of the software works correctly. Without well-structured test cases, testing becomes inconsistent โ two testers executing the same scenario will get different results, defects will be missed, and nobody can tell you exactly what has been tested and what has not. Writing good test cases is the skill that separates professional QA work from random clicking.
The Anatomy of a Test Case โ Every Field Has a Purpose
A professional test case contains several standard fields. Each field exists because it solves a real communication problem โ ambiguity about what to test, confusion about setup, or disputes about what the correct result should be.
# A well-structured test case as a Python data model
class TestCase:
def __init__(self, tc_id, title, module, priority,
preconditions, steps, expected_result,
test_data=None, requirement_id=None):
self.tc_id = tc_id # Unique identifier
self.title = title # One-line summary
self.module = module # Feature area
self.priority = priority # P1 (critical) to P4 (low)
self.preconditions = preconditions # What must be true BEFORE
self.steps = steps # Numbered actions
self.expected_result = expected_result # What SHOULD happen
self.test_data = test_data # Specific inputs
self.requirement_id = requirement_id # Traceability link
self.actual_result = None # Filled during execution
self.status = "Not Executed" # Pass / Fail / Blocked
def execute(self, actual):
self.actual_result = actual
self.status = "Pass" if actual == self.expected_result else "Fail"
return self.status
# Example: a complete test case for login
tc_login = TestCase(
tc_id="TC-LOGIN-001",
title="Verify successful login with valid credentials",
module="Authentication",
priority="P1",
preconditions="User account 'testuser@example.com' exists with password 'Secure@123'",
steps=[
"1. Navigate to https://staging.app.com/login",
"2. Enter 'testuser@example.com' in the Email field",
"3. Enter 'Secure@123' in the Password field",
"4. Click the 'Sign In' button",
],
expected_result="User is redirected to the Dashboard page; welcome message shows 'Hello, Test User'",
test_data={"email": "testuser@example.com", "password": "Secure@123"},
requirement_id="REQ-AUTH-001",
)
print(f"Test Case: {tc_login.tc_id}")
print(f"Title: {tc_login.title}")
print(f"Module: {tc_login.module}")
print(f"Priority: {tc_login.priority}")
print(f"Prereqs: {tc_login.preconditions}")
print(f"Steps: {len(tc_login.steps)} steps")
print(f"Expected: {tc_login.expected_result}")
print(f"Traces to: {tc_login.requirement_id}")
print(f"Status: {tc_login.status}")
# Simulate execution
result = tc_login.execute("User is redirected to the Dashboard page; welcome message shows 'Hello, Test User'")
print(f"\nAfter execution: {result}")
Common Mistakes
Mistake 1 โ Writing vague test steps that leave room for interpretation
โ Wrong: “1. Go to the app. 2. Log in. 3. Check it works.”
โ Correct: “1. Navigate to https://staging.app.com/login. 2. Enter ‘testuser@example.com’ in the Email field. 3. Enter ‘Secure@123’ in the Password field. 4. Click ‘Sign In’. 5. Verify the Dashboard page loads with the welcome message ‘Hello, Test User’.”
Mistake 2 โ Combining multiple verifications into a single test case
โ Wrong: One test case that checks login, profile update, password change and logout in a single sequence of 30 steps.
โ Correct: Separate test cases for each feature โ TC-LOGIN-001 for login, TC-PROFILE-001 for profile update, TC-PWD-001 for password change. Each case has one clear objective, making failures easy to isolate and report.