Testing does not happen all at once on the finished product. It happens in layers, starting from the smallest building blocks and expanding outward to the complete system. These layers are called test levels, and ISTQB defines four: unit, integration, system and acceptance. Each level catches a different category of defects, is performed by different people, and occurs at a different point in the SDLC. Skipping any level leaves a class of defects undetected until they surface in production.
The Four Test Levels — From Code to Customer
Think of test levels as a pyramid. Unit tests form the wide base — many small, fast tests that verify individual components. Integration tests check that components work together. System tests verify the complete application. Acceptance tests confirm the product meets business needs.
# The four ISTQB test levels with responsibilities and defect types
TEST_LEVELS = [
{
"level": "1. Unit Testing",
"scope": "Individual functions, methods, or classes in isolation",
"performed_by": "Developers",
"when": "During implementation — run on every code change",
"defects_caught": [
"Logic errors in a single function",
"Off-by-one errors in loops",
"Null pointer exceptions",
"Incorrect calculations",
],
"example": "Testing that calculate_tax(100, 0.08) returns 8.00",
"tools": "pytest, JUnit, NUnit, Jest",
},
{
"level": "2. Integration Testing",
"scope": "Interactions between two or more modules or services",
"performed_by": "Developers + QA Engineers",
"when": "After unit testing — when components are assembled",
"defects_caught": [
"API contract mismatches between services",
"Database query returning wrong data type",
"Authentication token not passed between modules",
"Message queue deserialization errors",
],
"example": "Testing that the Order Service correctly calls the Payment Gateway API",
"tools": "Postman, REST Assured, WireMock, testcontainers",
},
{
"level": "3. System Testing",
"scope": "The complete integrated application in a production-like environment",
"performed_by": "QA Engineers",
"when": "After integration testing — on a deployed build",
"defects_caught": [
"End-to-end workflow failures",
"Cross-module data inconsistencies",
"Environment-specific configuration errors",
"Performance bottlenecks under realistic load",
],
"example": "Testing the full checkout flow: browse → cart → payment → confirmation email",
"tools": "Selenium, Playwright, Cypress, JMeter",
},
{
"level": "4. Acceptance Testing (UAT)",
"scope": "Business requirements validated by end users or stakeholders",
"performed_by": "Business users, Product Owner, or customer",
"when": "Before release — final validation against user needs",
"defects_caught": [
"Features that work technically but do not meet business intent",
"Workflow gaps that developers did not anticipate",
"Usability issues from a non-technical perspective",
"Missing business rules not captured in requirements",
],
"example": "A store manager verifies that the inventory report matches their expectations",
"tools": "Manual testing, Cucumber/Gherkin for BDD-style acceptance",
},
]
for level in TEST_LEVELS:
print(f"\n{'='*55}")
print(f" {level['level']}")
print(f"{'='*55}")
print(f" Scope: {level['scope']}")
print(f" By: {level['performed_by']}")
print(f" When: {level['when']}")
print(f" Tools: {level['tools']}")
print(f" Example: {level['example']}")
print(f" Defects caught:")
for d in level['defects_caught']:
print(f" - {d}")
Common Mistakes
Mistake 1 — Relying solely on system-level tests and skipping unit tests
❌ Wrong: “We do not need unit tests because our Selenium suite covers everything.”
✅ Correct: “Our Selenium suite covers end-to-end flows, but unit tests catch logic errors 100x faster with instant feedback. A calculation bug that takes 3 minutes to reproduce through the UI is caught in milliseconds by a unit test.”
Mistake 2 — Treating UAT as a repeat of system testing
❌ Wrong: QA engineers run the same system test cases during UAT with no business user involvement.
✅ Correct: UAT involves actual business users or product owners who validate the software against their real-world workflows and expectations — not against technical test cases. QA facilitates UAT but does not own it.