Chapter 2: Testing in the Software Development Lifecycle
Introduction
Testing isn't a separate phase that happens at the end - it's integrated throughout the entire software development lifecycle (SDLC). This chapter explores when and how to test at each stage.
Software Development Models
Waterfall Model
Sequential approach where each phase must be completed before the next begins.
flowchart TD
A[Requirements] --> B[Design]
B --> C[Implementation]
C --> D[Testing]
D --> E[Deployment]
E --> F[Maintenance]
style A fill:#667eea
style B fill:#764ba2
style C fill:#f093fb
style D fill:#ff6b6b
style E fill:#ffd93d
style F fill:#6bcf7f
Testing in Waterfall:
- Testing phase comes after development
- Limited feedback loops
- Difficult to make changes
- High cost of late defect detection
V-Model (Verification and Validation)
Extends waterfall by adding corresponding test levels for each development phase.
graph LR
A[Requirements] -.->|validates| H[Acceptance Testing]
B[System Design] -.->|validates| G[System Testing]
C[Architectural Design] -.->|validates| F[Integration Testing]
D[Module Design] -.->|validates| E[Unit Testing]
A --> B --> C --> D
D --> E --> F --> G --> H
style A fill:#667eea
style B fill:#667eea
style C fill:#667eea
style D fill:#667eea
style E fill:#f093fb
style F fill:#f093fb
style G fill:#f093fb
style H fill:#f093fb
Advantages:
- Testing planned early
- Clear traceability
- Each level has corresponding tests
##Agile & Scrum
Iterative development with continuous testing.
flowchart LR
A[Product Backlog] --> B[Sprint Planning]
B --> C[Sprint 2-4 weeks]
C --> D[Daily Standup]
D --> C
C --> E[Sprint Review]
E --> F[Sprint Retrospective]
F --> G{More Sprints?}
G -->|Yes| A
G -->|No| H[Release]
style A fill:#667eea
style C fill:#764ba2
style E fill:#f093fb
style H fill:#6bcf7f
Testing in Agile:
- Continuous testing
- Test-Driven Development (TDD)
- Automated testing crucial
- Testers are part of the team
Test Levels
1. Unit Testing
Testing individual components in isolation.
# Example: Unit test for a function
def calculate_discount(price, discount_percent):
"""Calculate discounted price"""
if discount_percent < 0 or discount_percent > 100:
raise ValueError("Invalid discount percentage")
return price * (1 - discount_percent / 100)
# Unit test
def test_calculate_discount():
assert calculate_discount(100, 10) == 90
assert calculate_discount(100, 0) == 100
with pytest.raises(ValueError):
calculate_discount(100, -5)
Focus: Functions, methods, classes Who: Developers Tools: JUnit, pytest, NUnit
2. Integration Testing
Testing interactions between components.
graph TB
subgraph Integration Testing
A[User Service] -->|calls| B[Database]
A -->|calls| C[Email Service]
D[Payment Service] -->|calls| B
D -->|calls| E[Bank API]
end
style A fill:#667eea
style D fill:#667eea
style B fill:#764ba2
style C fill:#764ba2
style E fill:#764ba2
Focus: Module interactions, APIs, databases Who: Developers & Testers Approaches:
- Big Bang: Integrate all at once
- Top-Down: Start from main module
- Bottom-Up: Start from lowest modules
- Sandwich: Combination approach
3. System Testing
Testing the complete integrated system.
Types of System Tests:
- Functional: Feature verification
- Performance: Speed, scalability
- Security: Vulnerability testing
- Usability: User experience
- Compatibility: Different platforms
Example Test Scenarios:
### Test Scenario: E-commerce Checkout Flow
1. User browses products
2. Adds items to cart
3. Applies discount code
4. Enters shipping information
5. Selects payment method
6. Confirms order
7. Receives confirmation email
Expected: Complete purchase successfully
4. Acceptance Testing
Validation that system meets business requirements.
Types:
- User Acceptance Testing (UAT): Real users test
- Operational Acceptance Testing (OAT): Operations team verifies
- Regulatory Testing: Compliance verification
Test Types
Functional Testing
Verifies WHAT the system does.
def test_login_functionality():
"""Test login feature"""
# Test valid credentials
result = login("user@example.com", "Password123")
assert result.success == True
assert result.user_id is not None
# Test invalid credentials
result = login("user@example.com", "wrong_password")
assert result.success == False
assert result.error == "Invalid credentials"
Non-Functional Testing
Verifies HOW WELL the system performs.
Performance Testing Example:
import time
def test_response_time():
"""Test API response time"""
start_time = time.time()
response = api.get_user(user_id=123)
end_time = time.time()
response_time = end_time - start_time
assert response_time < 1.0 # Must respond within 1 second
Regression Testing
Ensures new changes don't break existing functionality.
flowchart LR
A[New Feature Added] --> B[Run Regression Tests]
B --> C{All Tests Pass?}
C -->|Yes| D[Deploy]
C -->|No| E[Fix Issues]
E --> B
style A fill:#667eea
style B fill:#764ba2
style D fill:#6bcf7f
style E fill:#ff6b6b
Maintenance Testing
Testing after software is deployed.
Triggers for Maintenance Testing:
- Bug fixes
- Enhancements
- Environment changes
- Data migration
Impact Analysis:
# Before changing a function, analyze impact
def analyze_function_usage(function_name):
"""Find all places where function is used"""
# This helps determine test scope
usages = search_codebase(function_name)
return [
"Module A: payment processing",
"Module B: reporting",
"Module C: admin dashboard"
]
# All these need regression testing!
Testing in DevOps
Continuous Integration and Continuous Deployment (CI/CD).
flowchart LR
A[Developer Commits] --> B[Build]
B --> C[Unit Tests]
C --> D[Integration Tests]
D --> E{Tests Pass?}
E -->|Yes| F[Deploy to Staging]
E -->|No| G[Notify Developer]
F --> H[System Tests]
H --> I{Tests Pass?}
I -->|Yes| J[Deploy to Production]
I -->|No| G
style A fill:#667eea
style F fill:#764ba2
style J fill:#6bcf7f
style G fill:#ff6b6b
Key Practices:
- Automated test execution
- Fast feedback loops
- Test at every stage
- Monitor production
Shift-Left Testing
Move testing earlier in the lifecycle.
Traditional:
Requirements ā Design ā Development ā [Testing Here] ā Release
Shift-Left:
[Testing] Requirements ā [Testing] Design ā [Testing] Development ā [Testing] Release
Benefits:
- Find defects earlier
- Lower cost of fixes
- Better quality
- Faster delivery
Test-Driven Development (TDD)
Write tests before code!
# 1. Write test first (it fails)
def test_add_user():
user = User("john@example.com")
assert user.email == "john@example.com"
assert user.is_active == True
# 2. Write minimum code to pass
class User:
def __init__(self, email):
self.email = email
self.is_active = True
# 3. Refactor (improve code while tests still pass)
class User:
def __init__(self, email):
self._validate_email(email)
self.email = email
self.is_active = True
def _validate_email(self, email):
if '@' not in email:
raise ValueError("Invalid email")
TDD Cycle:
flowchart LR
A[Write Test] --> B[Run Test Fails]
B --> C[Write Code]
C --> D[Run Test Passes]
D --> E[Refactor]
E --> A
style A fill:#667eea
style B fill:#ff6b6b
style D fill:#6bcf7f
style E fill:#764ba2
Practical Examples
Example 1: Testing in Agile Sprint
**Sprint Goal**: Implement user profile feature
Week 1:
- Day 1: Write acceptance criteria & tests
- Day 2-3: Develop feature with unit tests
- Day 4: Integration testing
- Day 5: Sprint review & demo
Test Activities:
- TDD for all new code
- Automated regression suite runs nightly
- Manual exploratory testing daily
- Performance test at sprint end
Example 2: CI/CD Pipeline
# .github/workflows/test.yml
name: Test Pipeline
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Run Unit Tests
run: pytest tests/unit
- name: Run Integration Tests
run: pytest tests/integration
- name: Generate Coverage Report
run: pytest --cov=src --cov-report=xml
- name: Check Coverage Threshold
run: |
if coverage < 80%; then
exit 1
fi
Key Takeaways
- ā Testing happens throughout the SDLC, not just at the end
- ā Different development models require different testing approaches
- ā Four test levels: Unit, Integration, System, Acceptance
- ā Test both functional AND non-functional requirements
- ā Shift testing left to find defects earlier
- ā Automate testing in CI/CD pipelines
- ā TDD improves code quality
Review Questions
- What is the V-Model and how does it improve on Waterfall?
- Explain the difference between unit and integration testing
- What is shift-left testing and why is it beneficial?
- How does testing differ in Agile vs. Waterfall?
- Describe the TDD cycle
Next Chapter
Chapter 3 explores Static Testing - finding defects without executing code through reviews and analysis!
Complete Lab 2 to practice testing at different levels in a real application!