Even the best-written test case is useless without the right test data to drive it, and meaningless if nobody can tell which requirement it verifies. Test data design ensures your inputs are realistic, comprehensive and cover the scenarios that matter. The Requirements Traceability Matrix (RTM) ensures every requirement has at least one test case covering it โ and every test case is justified by a requirement. Together, they transform your test suite from a collection of scripts into a structured quality assurance system.
Designing Test Data and Building an RTM
Test data is not an afterthought โ it is a first-class deliverable of the test design phase. Poor test data leads to false passes (the test “passed” because it used a trivially easy input) and missed defects (the edge case was never tested because no one created data for it).
# Test data design for a user registration feature
# Requirements:
# REQ-001: Username must be 3โ20 alphanumeric characters
# REQ-002: Email must be valid format (RFC 5322)
# REQ-003: Password must be 8โ64 chars with complexity rules
# REQ-004: Duplicate email must show error message
# โโ Test data table โโ
test_data_table = [
# (data_id, username, email, password, scenario, expected_req)
("TD-001", "alice", "alice@test.com", "Secure@1", "All valid โ happy path", "REQ-001,002,003"),
("TD-002", "ab", "ab@test.com", "Secure@1", "Username too short (2 chars)", "REQ-001"),
("TD-003", "a" * 21, "long@test.com", "Secure@1", "Username too long (21 chars)", "REQ-001"),
("TD-004", "alice", "not-an-email", "Secure@1", "Invalid email format", "REQ-002"),
("TD-005", "alice", "alice@test.com", "short", "Password below minimum", "REQ-003"),
("TD-006", "bob", "alice@test.com", "Secure@1", "Duplicate email", "REQ-004"),
("TD-007", "user_123", "user@test.com", "Secure@1", "Username with underscore", "REQ-001"),
("TD-008", "", "empty@test.com", "Secure@1", "Empty username", "REQ-001"),
]
# โโ Requirements Traceability Matrix (RTM) โโ
rtm = {
"REQ-001": {
"description": "Username must be 3-20 alphanumeric characters",
"test_cases": ["TC-REG-001", "TC-REG-002", "TC-REG-003", "TC-REG-007", "TC-REG-008"],
"status": "Covered",
},
"REQ-002": {
"description": "Email must be valid RFC 5322 format",
"test_cases": ["TC-REG-001", "TC-REG-004"],
"status": "Covered",
},
"REQ-003": {
"description": "Password must be 8-64 chars with complexity rules",
"test_cases": ["TC-REG-001", "TC-REG-005"],
"status": "Covered",
},
"REQ-004": {
"description": "Duplicate email must show error message",
"test_cases": ["TC-REG-006"],
"status": "Covered",
},
}
print("Requirements Traceability Matrix (RTM)")
print("=" * 70)
for req_id, info in rtm.items():
cases = ", ".join(info["test_cases"])
print(f" {req_id}: {info['description']}")
print(f" Tests: {cases} [{info['status']}]")
print()
# Check for uncovered requirements
uncovered = [r for r, info in rtm.items() if not info["test_cases"]]
if uncovered:
print(f"WARNING: Uncovered requirements: {uncovered}")
else:
print("All requirements have at least one test case โ")
Common Mistakes
Mistake 1 โ Using the same test data for every test case
โ Wrong: Every test case uses “john@test.com” and “Password123” regardless of what scenario it is testing.
โ Correct: Each test case uses data specifically designed for its scenario โ valid data for positive tests, boundary data for boundary tests, and deliberately malformed data for negative tests.
Mistake 2 โ Building the RTM after testing is complete
โ Wrong: Creating the traceability matrix retroactively as a documentation exercise after all tests have been executed.
โ Correct: Building the RTM during the test design phase so gaps in coverage are visible before execution begins. If a requirement has zero test cases mapped to it, you know immediately that additional test cases are needed.