Test Design Techniques: Static and Code Analysis π
The Story of the Bug Detective Agency π΅οΈ
Imagine you run a Bug Detective Agency. Your job? Find problems in buildings before people move in. You have two ways to work:
- Walk around and look (without touching anything) β this is Static Testing
- Actually live in the house and use everything β this is Dynamic Testing
Both are super important! Letβs explore how testers become the best bug detectives in the software world.
π Static Testing Overview
What Is Static Testing?
Think of it like reading a recipe before cooking. You donβt turn on the stove yet. You just read and check:
- βWait, it says add 10 cups of salt. Thatβs way too much!β
- βHmm, step 3 comes before step 2. Thatβs wrong!β
Static testing = Finding bugs WITHOUT running the code.
graph TD A[π Code Written] --> B[π Review & Inspect] B --> C{Found Bugs?} C -->|Yes| D[π§ Fix Now] C -->|No| E[β Ready for Next Step] D --> B
Why Is Static Testing Awesome?
| Benefit | Why It Matters |
|---|---|
| π Fast | Find bugs before running anything |
| π° Cheap | Fixing early costs less |
| π― Catches logic errors | Spots wrong thinking |
| π Improves understanding | Everyone learns the code |
Simple Example
Bad Code (spotted statically):
def divide(a, b):
return a / b # What if b is 0?
A reviewer reads this and says: βHey, what happens if someone passes 0 for b? It will crash!β
Fixed:
def divide(a, b):
if b == 0:
return "Cannot divide by zero!"
return a / b
No code was run. The bug was found just by reading!
π Dynamic Testing
What Is Dynamic Testing?
Now you actually cook the recipe. Turn on the stove. Mix ingredients. Taste the food!
Dynamic testing = Running the code and checking if it works correctly.
graph TD A[π Run the Program] --> B[π₯ Give It Input] B --> C[π€ Check Output] C --> D{Correct?} D -->|Yes| E[β Test Passed] D -->|No| F[π Bug Found!]
Real-Life Example
You have a login page. Dynamic testing means:
- Type a username and password
- Click the login button
- See what happens
| Test Case | Input | Expected Result | Actual Result |
|---|---|---|---|
| Valid login | user: βsamβ, pass: β1234β | Welcome Sam! | Welcome Sam! β |
| Wrong password | user: βsamβ, pass: βwrongβ | Error message | Error message β |
| Empty fields | user: ββ, pass: ββ | Please fill all fields | Crashed! π |
Static vs Dynamic: The Difference
Think of buying a used car:
| Static Testing | Dynamic Testing |
|---|---|
| π Look at the car | π Test drive the car |
| Read the manual | Feel how it drives |
| Check for rust spots | Hear if engine sounds weird |
| Look at oil color | See if brakes work |
You need BOTH! Looking finds some problems. Driving finds others.
π¬ Static Code Analysis
What Is Static Code Analysis?
Imagine you have a robot helper that reads your code and says:
- βHey, you made a spelling mistake in this variable!β
- βThis line will never run!β
- βYou forgot to close the file!β
Static Code Analysis = Using tools to automatically scan code for problems.
graph TD A[π Your Code] --> B[π€ Analysis Tool] B --> C[π Report] C --> D[β οΈ Warnings] C --> E[β Errors] C --> F[π‘ Suggestions]
What Do These Tools Find?
| Problem Type | Example | Why Itβs Bad |
|---|---|---|
| Unused variables | x = 5 (never used) |
Wastes memory, confuses readers |
| Dead code | Code after return |
Never runs, misleading |
| Security issues | Using eval() |
Hackers can attack |
| Style problems | Inconsistent spacing | Hard to read |
Popular Static Analysis Tools
| Language | Tool | What It Catches |
|---|---|---|
| Python | PyLint, Flake8 | Style, errors, complexity |
| JavaScript | ESLint | Bugs, bad practices |
| Java | SonarQube | Security, bugs, smells |
| C/C++ | Cppcheck | Memory leaks, bugs |
Simple Example
Code with issues:
function greet(name) {
var unused = 42;
console.log("Hello " + nme);
return;
console.log("Goodbye");
}
Static analyzer output:
Line 2: 'unused' is declared but never used
Line 3: 'nme' is not defined (did you mean 'name'?)
Line 5: Unreachable code after return
You havenβt run the code, but the tool found 3 bugs!
π₯ Code Review Techniques
What Is Code Review?
Remember group projects at school? Before submitting, your friend reads your work and says:
- βThis sentence doesnβt make sense.β
- βYou repeated this paragraph twice.β
- βThis answer is wrong!β
Code Review = Team members read each otherβs code to find problems and share knowledge.
graph TD A[π¨βπ» Developer Writes Code] --> B[π€ Submits for Review] B --> C[π Reviewer Reads Code] C --> D{Approved?} D -->|No| E[π¬ Comments & Suggestions] E --> F[π§ Developer Fixes] F --> B D -->|Yes| G[β Code Merged]
Types of Code Review
| Type | How It Works | Best For |
|---|---|---|
| Pair Programming | Two people code together | Learning, complex problems |
| Over-the-Shoulder | Reviewer watches coder | Quick informal checks |
| Tool-Based (Pull Request) | Review on GitHub/GitLab | Remote teams, async work |
| Formal Inspection | Scheduled meetings, checklists | Critical systems |
What Reviewers Look For
- Correctness β Does it work right?
- Readability β Can others understand it?
- Efficiency β Is it fast enough?
- Security β Is it safe from hackers?
- Standards β Does it follow team rules?
Example Code Review Comment
Original code:
def calc(x, y, z):
return x + y * z
Reviewer comments:
π¬ βWhat do x, y, and z represent? Please use meaningful names like
base,rate,hours. Also, should multiplication happen before addition? Add parentheses to make it clear:base + (rate * hours)β
Improved code:
def calculate_pay(base, rate, hours):
return base + (rate * hours)
Code Review Checklist Example
- [ ] Does the code do what itβs supposed to?
- [ ] Are variable names clear?
- [ ] Are there any bugs I can spot?
- [ ] Is there duplicate code that could be simplified?
- [ ] Are error cases handled?
- [ ] Is it easy to read and understand?
𧬠Mutation Testing
What Is Mutation Testing?
Hereβs a fun story! Imagine you have a guard dog. You want to know: Is this dog actually good at catching burglars?
So you send fake burglars! If the dog catches them, itβs a good dog. If fake burglars sneak past, your dog needs training.
Mutation Testing = Making tiny changes to your code (mutations) to see if your tests catch them.
graph TD A[π Original Code] --> B[𧬠Create Mutants] B --> C[πΎ Mutant 1] B --> D[πΎ Mutant 2] B --> E[πΎ Mutant 3] C --> F[π§ͺ Run Tests] D --> F E --> F F --> G{Test Failed?} G -->|Yes| H[β Mutant Killed - Good Test!] G -->|No| I[π± Mutant Survived - Weak Test!]
How Mutation Testing Works
Step 1: Start with working code and a test
# Original code
def is_adult(age):
return age >= 18
# Test
def test_is_adult():
assert is_adult(20) == True
Step 2: Create mutants (tiny changes)
| Mutant | Change Made | New Code |
|---|---|---|
| Mutant 1 | Change >= to > |
return age > 18 |
| Mutant 2 | Change >= to <= |
return age <= 18 |
| Mutant 3 | Change 18 to 17 |
return age >= 17 |
Step 3: Run tests against each mutant
| Mutant | Test Result | Verdict |
|---|---|---|
| Mutant 1 | is_adult(20) still returns True |
π± SURVIVED! |
| Mutant 2 | is_adult(20) returns False, test fails |
β KILLED! |
| Mutant 3 | is_adult(20) still returns True |
π± SURVIVED! |
Problem found! Two mutants survived. Our test isnβt good enough!
Better test:
def test_is_adult():
assert is_adult(20) == True
assert is_adult(18) == True # Boundary!
assert is_adult(17) == False # Below boundary!
Now all mutants would be killed!
Mutation Score
Mutation Score = (Killed Mutants / Total Mutants) Γ 100%
| Score | Meaning |
|---|---|
| 100% | Perfect! All mutants caught |
| 80%+ | Good test suite |
| 60-80% | Needs improvement |
| Below 60% | Tests are too weak |
Common Mutation Types
| Mutation Type | Original | Mutated |
|---|---|---|
| Arithmetic | a + b |
a - b |
| Relational | x > y |
x >= y |
| Logical | a && b |
a || b |
| Constant | return 0 |
return 1 |
| Negation | if (x) |
if (!x) |
π― Bringing It All Together
Hereβs how all these techniques work together in the real world:
graph TD A[π¨βπ» Developer Writes Code] --> B[π€ Static Analysis] B --> C[π₯ Code Review] C --> D[π§ͺ Write Tests] D --> E[π Dynamic Testing] E --> F[𧬠Mutation Testing] F --> G{All Good?} G -->|Yes| H[π Deploy!] G -->|No| A
Quick Comparison
| Technique | When | Who/What | Finds |
|---|---|---|---|
| Static Testing | Before running | Humans reading | Logic errors, design flaws |
| Dynamic Testing | While running | Running the code | Runtime bugs, crashes |
| Static Code Analysis | Anytime | Automated tools | Style, security, common bugs |
| Code Review | Before merging | Team members | All kinds of issues |
| Mutation Testing | After writing tests | Special tools | Weak tests |
π Key Takeaways
- Static Testing is like proofreading β find mistakes without running code
- Dynamic Testing is like test-driving β run the code and check it works
- Static Code Analysis uses robots to scan your code automatically
- Code Review is teamwork β humans checking each otherβs work
- Mutation Testing tests your tests β makes sure your safety net has no holes
Remember: Great testers use ALL these tools! Like a detective using magnifying glass, fingerprint kit, AND asking witnesses. Each technique catches different types of bugs.
Youβre now ready to be a Bug Detective! Go find those bugs before users do! π