A product does not appear as one solid block. Developers write functions, join functions into components, components call one another, several services join into a system, and eventually that system reaches users or operations.
Testing a fee-calculating function and testing an entire money-transfer flow obviously cannot be done in exactly the same way. The object is different, the bugs we want are different and the environment is different. That is where test levels come from.
What is a test level?
A test level is a group of test activities organised and managed together. Each level is associated with a particular version of the test object, from a small piece of code to the whole system in its usage context.
CTFL 4.0.1 uses five levels:
- Component testing
- Component integration testing
- System testing
- System integration testing
- Acceptance testing
The old version of this article combined the two integration levels as Integration Test, which is still common project language. I am separating them this time so two people do not both say “integration” while one is testing between two classes and the other is testing between a banking app and a payment gateway.

The V-Model is an easy way to see how test levels relate to corresponding work products. It does not mean every project must finish one branch before any testing can happen.
Component testing
Component testing, often simply called unit testing, checks a component in a relatively isolated state. A component may be a function, class, module or a larger unit, depending on the architecture.
Suppose we have a fee function:
def fee(amount):
if amount <= 0:
raise ValueError("amount must be positive")
return amount * 0.01
At this level we care about things such as:
- Valid input returns the correct result.
- Boundaries and invalid input are handled correctly.
- The exception is the right type and occurs at the right time.
- Important code branches have been exercised.
Developers usually own this work because they are closest to the code and can fix it immediately. Testers may contribute when the team needs testing skills or has component tests at a higher level. The important thing is not stamping this “developer work” or “tester work”; the component needs a fence before it is joined to anything else.
Typical question: Who is responsible for component testing?
Developers usually own most component tests because they work closest to the code. Testers may also contribute depending on the product, skills and required independence.
Component integration testing
Two components working perfectly on their own does not tell us that they will work when they call each other.
Component A sends amount as a decimal while component B parses an integer. A service times out after 10 seconds while the client gives up at three. The sender calls the field userId while the receiver waits for customer_id. This is component integration testing territory.
Common failures include:
- Incorrect data format, encoding or mapping.
- The wrong order or timing of calls.
- Missing handling for timeout, retry or partial failure.
- Different interpretations of the contract and error response.
- State being updated halfway and then abandoned.
Developers can test with unit/component tests using fakes, stubs or test containers. Testers can test an API or an integrated build. Whether there is a frontend is not the boundary that decides who may do the work.
Big bang and incremental
Big bang joins many parts and then tests once. The initial setup may look easier, but when a failure appears, narrowing down the culprit is miserable.
Incremental integration joins pieces gradually. If a dependency is not ready, the team can use:
- A stub on the called side that returns fake data.
- A driver on the calling side that triggers the component under test.
- Service virtualisation, a fake server or a modern mock can solve the same family of problem.
Teams integrate continuously these days, but CI does not automatically mean good integration testing. A green pipeline proves only that the tests that were written are green.
System testing
System testing checks the behaviour and quality characteristics of a relatively complete system. The test basis may include requirements, user stories, use cases, risks, specifications and rules agreed by the team.
Here we see flows such as:
- Register → verify email → log in.
- Create an order → pay → reduce inventory → send a notification.
- Permissions between a user, moderator and admin.
- System performance, security, usability or compatibility.
An independent test team often performs most system testing because the viewpoint outside the implementation is valuable. But “only testers perform System Testing” is too strong. Developers, specialists or another team may contribute; the context and required independence decide.
Typical question: What is system testing?
System testing verifies the behaviour and quality characteristics of a complete system against its test basis in an environment representative of its intended use.
System integration testing
System integration testing checks the interface between the system under test and another system or external service.
Take an e-commerce app calling a payment gateway:
- Does the payment request follow the contract?
- If the gateway returns success but the callback is late, what state is the order in?
- If a callback is sent twice, do we charge or create the order twice?
- If the gateway dies, how does the app retry and what does the user see?
This is often grouped under integration testing or system testing, which is perfectly normal. As scope grows, the separate name helps the team say whether it is testing inside one system or the connection between several.
Acceptance testing
Acceptance testing assesses whether the system is ready to be accepted, deployed or used. It focuses on business needs, operational processes, contractual obligations or regulations, depending on the context.
Participants may be end users, customers, Product Owners, business representatives, operations teams or an authority. Testers often support the environment, data and scenarios and record issues. The decision to accept belongs to the authorised party, not automatically to the tester.
Common forms include:
| Form | Focus |
|---|---|
| User acceptance testing | Can users or the business complete real work? |
| Operational acceptance testing | Are backup, restore, monitoring, operations and support ready? |
| Contractual acceptance testing | Does the product meet the contract's acceptance criteria? |
| Regulatory acceptance testing | Does the product meet relevant regulations? |
| Alpha/Beta testing | Gather feedback from users in a controlled or real-world environment. |
UAT is not a customer clicking a few things and signing because the deadline has arrived. If acceptance criteria are vague or the environment does not resemble real operation, the signature only closes paperwork; it does not reduce system risk.
Do we always go from low to high?
In terms of dependency, a component must exist before it can be joined into a system. But test activities do not have to queue in absolute order.
While developers write component tests, testers can review acceptance criteria, design system tests, prepare data and work on contracts. Some levels run in parallel or repeat for each increment. Do not look at the V-Model and turn it into a concrete waterfall.
Closing
| Level | Short question |
|---|---|
| Component | Does this isolated part run correctly? |
| Component integration | Do the joined code parts follow their contract? |
| System | Does the whole system meet the test basis? |
| System integration | Does the system communicate correctly with other systems? |
| Acceptance | Is the product ready for the authorised party to accept? |
Knowing the level helps identify the test object, objective, environment and people who need to participate. The next article is Test Plan: before testing all these things, the team has to agree on scope, resources, risks and stop conditions.