Test Levels — Unit, Integration, System and Acceptance Testing

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}")
Note: The “test pyramid” concept — many unit tests at the base, fewer integration tests in the middle, fewer system tests at the top — reflects the ideal distribution of testing effort. Unit tests are fast, cheap and precise. System tests are slow, expensive and broad. A healthy test suite has thousands of unit tests, hundreds of integration tests, and dozens of critical end-to-end system tests. Inverting this pyramid (many slow UI tests, few unit tests) leads to slow feedback cycles and fragile test suites.
Tip: As a QA engineer, you will primarily work at the system and acceptance levels. However, understanding unit and integration testing helps you collaborate with developers more effectively. When a system test fails, knowing whether the root cause is likely a unit-level bug (bad logic in one function) or an integration-level bug (two services not communicating correctly) helps you write more targeted defect reports and reduces the developer’s debugging time.
Warning: Do not confuse integration testing with system testing. Integration testing verifies that two specific modules work together correctly (e.g., the Order Service talks to the Payment API). System testing verifies that the entire application works end-to-end in a realistic environment. Running a single API call in Postman is integration testing. Walking through a full checkout flow in a browser on a staging server is system testing. The distinction matters because they catch different types of defects.

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.

🧠 Test Yourself

Which test level is primarily responsible for catching defects in the interaction between two services, such as an Order Service failing to correctly pass data to a Payment Gateway API?