🏗️ Testing Types: Test Levels
The Building Inspector’s Guide to Software Quality
Imagine you’re building a LEGO castle. Would you wait until the entire castle is finished to check if pieces fit together? Of course not! You’d check each brick, then each tower, then how towers connect, and finally the whole castle.
Software testing works the same way. We test in layers—from tiny pieces to the complete product. Let’s explore each level!
🧱 Unit Testing
Checking Each LEGO Brick
Think of unit testing like checking a single LEGO brick. Does it have all its bumps? Is it the right color? Can it connect to other bricks?
In code: A “unit” is the smallest testable piece—usually one function or method.
// The function we're testing
function add(a, b) {
return a + b;
}
// Unit test
test('add 2 + 3 equals 5', () => {
expect(add(2, 3)).toBe(5);
});
Why it matters:
- 🐛 Catches bugs early (when they’re cheap to fix)
- 📝 Acts as living documentation
- 💪 Gives developers confidence to make changes
Real-life example: Testing if your calculator’s “+” button adds numbers correctly—without caring about the display, memory, or other buttons.
🔗 Integration Testing
Making Sure LEGO Pieces Snap Together
Now that each brick works alone, do they work together? Integration testing checks if different parts of your software play nicely with each other.
Simple Example: Your app has:
- A login function (checks username/password)
- A database (stores user info)
Integration testing asks: “When login calls the database, does the whole flow work?”
// Integration test
test('user can log in with valid credentials',
async () => {
const result = await loginService.login(
'alice',
'password123'
);
expect(result.success).toBe(true);
expect(result.user.name).toBe('Alice');
});
The key difference from unit testing:
| Unit Test | Integration Test |
|---|---|
| Tests ONE piece in isolation | Tests pieces TOGETHER |
| Uses fake/mock dependencies | Uses REAL dependencies |
| Super fast | Slower (real connections) |
🎯 Integration Testing Strategies
Different Ways to Build Your LEGO Castle
There are three main strategies. Think of them as different ways to assemble your castle:
1. 🔝 Top-Down Integration
Start from the roof, work down
Test the main controller first, then add lower modules one by one.
graph TD A[Main App] --> B[Service Layer] B --> C[Database Layer] style A fill:#4CAF50 style B fill:#FFC107 style C fill:#9E9E9E
- ✅ Tests critical paths early
- ❌ Need “stubs” for modules not ready yet
2. 🔝 Bottom-Up Integration
Start from the foundation, work up
Test database layer first, then services, then the main app.
graph TD A[Main App] --> B[Service Layer] B --> C[Database Layer] style A fill:#9E9E9E style B fill:#FFC107 style C fill:#4CAF50
- ✅ No stubs needed
- ❌ UI tested last (user can’t see progress)
3. 🥪 Sandwich/Big Bang Integration
Build sections, then combine
Test in chunks, then integrate everything at once.
- ✅ Parallel development possible
- ❌ Hard to isolate bugs when combined
🧩 Component Testing
Testing a Complete LEGO Tower
A component is bigger than a unit but smaller than the whole system. Think of it as a complete tower in your castle—it has walls, windows, and a roof working together.
Example: Testing your app’s “Shopping Cart” component:
- Can add items
- Can remove items
- Calculates total correctly
- Saves between sessions
// Component test for ShoppingCart
test('cart calculates total with discount',
() => {
const cart = new ShoppingCart();
cart.add({ name: 'Book', price: 20 });
cart.add({ name: 'Pen', price: 5 });
cart.applyDiscount(10); // 10% off
expect(cart.total).toBe(22.50);
});
Component vs Unit vs Integration:
| Level | Scope | Example |
|---|---|---|
| Unit | Single function | calculatePrice() |
| Component | Feature module | ShoppingCart class |
| Integration | Multiple modules | Cart + Payment + Database |
🏰 System Testing
Testing the Complete LEGO Castle
Now we test the entire application as one complete system. Does everything work together in the real environment?
What we check:
- ✅ All features work end-to-end
- ✅ Performance under load
- ✅ Security vulnerabilities
- ✅ Works on different devices/browsers
Example: Testing an e-commerce website:
- User browses products ✓
- User adds to cart ✓
- User checks out ✓
- Payment processes ✓
- Confirmation email sent ✓
graph LR A[Browser] --> B[Web Server] B --> C[App Server] C --> D[Database] C --> E[Payment API] C --> F[Email Service]
The environment matters! System testing happens in an environment that mirrors production—same databases, same servers, same everything (just with test data).
🛤️ End-to-End Testing
Walking Through the Castle from Gate to Throne
E2E testing simulates a real user journey from start to finish. We use automated tools that click buttons, fill forms, and navigate—just like a human would.
Example: User Registration Flow
// E2E test with Cypress
describe('User Registration', () => {
it('creates new account successfully',
() => {
cy.visit('/register');
cy.get('#name').type('Alice');
cy.get('#email').type('alice@test.com');
cy.get('#password').type('SecurePass123');
cy.get('#submit').click();
cy.url().should('include', '/dashboard');
cy.contains('Welcome, Alice!');
});
});
E2E vs System Testing:
| System Testing | E2E Testing |
|---|---|
| Tests ALL features work | Tests SPECIFIC user journeys |
| Technical focus | User behavior focus |
| May test APIs directly | Always through the UI |
✅ Acceptance Testing
Does the Castle Meet the King’s Requirements?
The castle is built. But does it have everything the king asked for? Acceptance testing verifies the software meets business requirements.
Two key questions:
- Does it do what we agreed it would do?
- Is it ready for release?
Example Requirements Checklist:
- [ ] Users can reset forgotten passwords
- [ ] Search returns results in < 2 seconds
- [ ] Works on iOS and Android
- [ ] Supports 10,000 concurrent users
Types of Acceptance Criteria:
GIVEN a registered user
WHEN they enter wrong password 3 times
THEN their account is locked for 15 minutes
AND they receive an email notification
This is called Gherkin syntax—readable by both developers and business people!
👑 User Acceptance Testing (UAT)
The King Inspects His Castle
The final checkpoint! Real users (or their representatives) test the software in real-world conditions.
Who does UAT?
- Business stakeholders
- End users (beta testers)
- Client representatives
NOT the development team!
The UAT Process:
graph TD A[Define Test Scenarios] --> B[Select Testers] B --> C[Execute Tests] C --> D{All Pass?} D -->|Yes| E[Sign-off & Release] D -->|No| F[Report Issues] F --> G[Fix & Retest] G --> C
Real Example: Banking App UAT
- Bank employees test with real scenarios
- “Transfer $100 to savings”
- “View last 30 transactions”
- “Set up recurring payment”
If they approve → Software goes live! 🚀
🎯 The Complete Testing Pyramid
All these test levels form a pyramid:
/\
/ \ 👑 UAT
/----\ ✅ Acceptance
/------\ 🛤️ E2E
/--------\ 🏰 System
/----------\ 🧩 Component
/------------\ 🔗 Integration
/--------------\ 🧱 Unit Tests
More tests at the bottom, fewer at the top. Unit tests are fast and cheap. E2E and UAT are slow and expensive.
🌟 Quick Summary
| Level | What | Who | When |
|---|---|---|---|
| Unit | Single functions | Developers | During coding |
| Integration | Connected modules | Developers | After units pass |
| Component | Feature modules | Dev/QA | After integration |
| System | Whole application | QA team | Before release |
| E2E | User journeys | QA team | Before release |
| Acceptance | Requirements | Stakeholders | Before sign-off |
| UAT | Real-world use | End users | Final checkpoint |
💡 Remember This!
Testing is like quality control in a factory. You check parts (unit), connections (integration), assemblies (component), the whole product (system), customer experience (E2E), and finally—does the customer approve? (UAT)
Each level catches different bugs. Skip any level, and bugs slip through to your users.
Your software is only as strong as your weakest test level! 🏆